您的位置:首页 > 其它

装饰(Decorator)模式

2007-02-05 16:53 375 查看


意图:

Decorator模式采用对象组合而非继承的手法,实现了在运行时动态的扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的
“灵活性差”和“多子类衍生问题”。同时它很好地符合面向对象设计原则中“优先使用对象组合而非继承”和“开放-封闭”原则。

结构:

装饰模式的类图如下图所示:



在装饰模式中的各个角色有:

抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。

具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。

装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加的责任。

Decorator模式的几个要点(李建忠老师讲义):


通过采用组合、而继承的手法,Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。

Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类
应该透明--换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。


Decorator类在接口上表现为is-a
Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a
Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装
饰”一个Component对象,且装饰后的对象仍然是一个Component对象。


Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”--是为“装饰”的含义。

示意性代码:


1
// Decorator pattern -- Structural example
2
3 using System;
4
5 namespace DoFactory.GangOfFour.Decorator.Structural
6 {
7
8 // MainApp test application
9
10 class MainApp
11 {
12 static void Main()
13 {
14 // Create ConcreteComponent and two Decorators
15 ConcreteComponent c = new ConcreteComponent();
16 ConcreteDecoratorA d1 = new ConcreteDecoratorA();
17 ConcreteDecoratorB d2 = new ConcreteDecoratorB();
18
19 // Link decorators
20 d1.SetComponent(c);
21 d2.SetComponent(d1);
22
23 d2.Operation();
24
25 // Wait for user
26 Console.Read();
27 }
28 }
29
30 // "Component"
31
32 abstract class Component
33 {
34 public abstract void Operation();
35 }
36
37 // "ConcreteComponent"
38
39 class ConcreteComponent : Component
40 {
41 public override void Operation()
42 {
43 Console.WriteLine("ConcreteComponent.Operation()");
44 }
45 }
46
47 // "Decorator"
48
49 abstract class Decorator : Component
50 {
51 protected Component component;
52
53 public void SetComponent(Component component)
54 {
55 this.component = component;
56 }
57
58 public override void Operation()
59 {
60 if (component != null)
61 {
62 component.Operation();
63 }
64 }
65 }
66
67 // "ConcreteDecoratorA"
68
69 class ConcreteDecoratorA : Decorator
70 {
71 private string addedState;
72
73 public override void Operation()
74 {
75 base.Operation();
76 addedState = "New State";
77 Console.WriteLine("ConcreteDecoratorA.Operation()");
78 }
79 }
80
81 // "ConcreteDecoratorB"
82
83 class ConcreteDecoratorB : Decorator
84 {
85 public override void Operation()
86 {
87 base.Operation();
88 AddedBehavior();
89 Console.WriteLine("ConcreteDecoratorB.Operation()");
90 }
91
92 void AddedBehavior()
93 {
94 }
95 }
96 }
97

.net中的装饰模式实例(Stream):

结构图:



可以看到BufferedStream、CryptoStream其实就是两个包装类,这里的Decorator模式省略了抽象装饰模式(Decorator),示例代码如下:

1 class Program
2 {
3  public static void Main(string[] args)
4  {
5   MemoryStream ms =new MemoryStream(new byte[] { 100,456,864,222,567});
6
7   //扩展了缓冲的功能
8
9   BufferedStream buff = new BufferedStream(ms);

  //扩展了缓冲,加密的功能

  CryptoStream crypto = new CryptoStream(buff);
 }
}

查看BufferedStream类的参考代码,可以看到BufferedStream类继承了Stream,并在现有流上增加了缓存功能。部分代码如下:

1 using System;
2 using System.Runtime.InteropServices;
3
4 namespace System.IO
5 {
6 // 摘要:
7 // 给另一流上的读写操作添加一个缓冲层。无法继承此类。
8 [ComVisible(true)]
9 public sealed class BufferedStream : Stream
{
// 摘要:
// 使用默认的缓冲区大小 4096 字节初始化 System.IO.BufferedStream 类的新实例。
//

public BufferedStream(Stream stream);
//
// 摘要:
// 使用指定的缓冲区大小初始化 System.IO.BufferedStream 类的新实例。
//

public BufferedStream(Stream stream, int bufferSize);

// 摘要:
// 获取一个值,该值指示当前流是否支持读取。
//
public override bool CanRead { get; }
public override bool CanSeek { get; }
public override bool CanWrite { get; }
public override long Length { get; }
public override long Position { get; set; }

protected override void Dispose(bool disposing);
//
// 摘要:
// 清除该流的所有缓冲区,使得所有缓冲的数据都被写入到基础设备。
//
public override int Read(byte[] array, int offset, int count);
//
// 摘要:
// 从基础流中读取一个字节,并返回转换为 int 的该字节;或者如果从流的末尾读取则返回 -1。
//
public override int ReadByte();
//
// 摘要:
// 设置当前缓冲流中的位置。
//

public override long Seek(long offset, SeekOrigin origin);
//
// 摘要:
// 设置缓冲流的长度。
//

public override void SetLength(long value);
//
// 摘要:
// 将字节复制到缓冲流,并将缓冲流内的当前位置前进写入的字节数。
//

public override void Write(byte[] array, int offset, int count);
//
// 摘要:
// 将一个字节写入缓冲流的当前位置。
//

public override void WriteByte(byte value);
}
}

实际应用:

适用场合:

需要扩展一个类的功能,或给一个类增加附加责任。

需要动态地给一个对象增加功能,这些功能可以再动态地撤销。

需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。

实例(为用户登录行为增加权限验证的职责和添加日志记录功能):

结构图:



实例代码:

public class User
{
public virtual void SignIn()
{
Console.WriteLine(”The User Sign In.”);
}
}

抽象类SignInDecorator的代码如下所示:

1 public abstract SignInDecorator:User
2 {
3 private User m_user;
4 public User User
5 {
6 get {return m_user;}
7 set {m_user = value;}
8 }
9 public override void SignIn()
{
if (m_user != null)
{
m_user.SignIn();
}
}
}

而SignInDecorator的子类SecuritySignInDecorator (扩展验证功能)定义则如下:

1 public class SecuritySignInDecorator:SignInDecorator
2 {
3 public override void SignIn()
4 {
5 if (IsValid())
6 {
7 base.SignIn();
8 }
9 else
{
throw new NotAuthorizationException();
}
}
private bool IsValid()
{
//略;
return true;
}
}

而SignInDecorator的子类LoggingSignInDecorator
(增加日志功能)定义则如下:

1 public class LoggingSignInDecorator:SignInDecorator
2 {
3 public override void SignIn()
4 {
5 base.SignIn();
6 Logging();
7 }
8 private void Logging()
9 {
//略;
}
}

参考文献:
《.net与设计模式》
《C#面向对象设计模式纵横谈系列课程》
《java与模式》等
当然还有博客园中众多前辈的随笔,这里就不一一指出还望见谅……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: