C#设计模式(12)-Decorator Pattern
一、 装饰(Decorator)模式
装饰(Decorator)模式又名包装(Wrapper)模式[GOF95]。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
引言
孙悟空有七十二般变化,他的每一种变化都给他带来一种附加的本领。他变成鱼儿时,就可以到水里游泳;他变成雀儿时,就可以在天上飞行。而不管悟空怎么变化,在二郎神眼里,他永远是那只猢狲。
装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。
二、 装饰模式的结构
装饰模式使用原来被装饰的类的一个子类的实例,把客户端的调用委派到被装饰类。装饰模式的关键在于这种扩展是完全透明的。
在孙猴子的例子里,老孙变成的鱼儿相当于老孙的子类,这条鱼儿与外界的互动要通过"委派",交给老孙的本尊,由老孙本尊采取行动。
装饰模式的类图如下图所示:
在装饰模式中的各个角色有:
- 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
- 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
- 具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加的责任。
三、 装饰模式示例性代码
以下示例性代码实现了装饰模式:
using System;
// "Component"
abstract class Component
{
// Methods
abstract public void Operation();
}
// "ConcreteComponent"
class ConcreteComponent : Component
{
// Methods
override public void Operation()
{
Console.WriteLine("ConcreteComponent.Operation()");
}
}
// "Decorator"
abstract class Decorator : Component
{
// Fields
protected Component component;
// Methods
public void SetComponent( Component component )
{
this.component = component;
}
override public void Operation()
{
if( component != null )
component.Operation();
}
}
// "ConcreteDecoratorA"
class ConcreteDecoratorA : Decorator
{
// Fields
private string addedState;
// Methods
override public void Operation()
{
base.Operation();
addedState = "new state";
Console.WriteLine("ConcreteDecoratorA.Operation()");
}
}
// "ConcreteDecoratorB"
class ConcreteDecoratorB : Decorator
{
// Methods
override public void Operation()
{
base.Operation();
AddedBehavior();
Console.WriteLine("ConcreteDecoratorB.Operation()");
}
void AddedBehavior()
{
}
}
/// <summary>
/// Client test
/// </summary>
public class Client
{
public static void Main( string[] args )
{
// Create ConcreteComponent and two Decorators
ConcreteComponent c = new ConcreteComponent();
ConcreteDecoratorA d1 = new ConcreteDecoratorA();
ConcreteDecoratorB d2 = new ConcreteDecoratorB();
// Link decorators
d1.SetComponent( c );
d2.SetComponent( d1 );
d2.Operation();
}
}
上面的代码在执行装饰时是通过SetComponent方法实现的,在实际应用中,也有通过构造函数实现的,一个典型的创建过程可能如下:
new Decorator2(
new Decorator3(
new ConcreteComponent()
)
)
)
装饰模式常常被称为包裹模式,就是因为每一个具体装饰类都将下一个具体装饰类或者具体构件类包裹起来。
四、 装饰模式应当在什么情况下使用
在以下情况下应当使用装饰模式:
- 需要扩展一个类的功能,或给一个类增加附加责任。
- 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。
五、 装饰模式实际应用的例子
该例子演示了通过装饰模式为图书馆的图书与录像带添加"可借阅"装饰。
using System;
using System.Collections;
// "Component"
abstract class LibraryItem
{
// Fields
private int numCopies;
// Properties
public int NumCopies
{
get{ return numCopies; }
set{ numCopies = value; }
}
// Methods
public abstract void Display();
}
// "ConcreteComponent"
class Book : LibraryItem
{
// Fields
private string author;
private string title;
// Constructors
public Book(string author,string title,int numCopies)
{
this.author = author;
this.title = title;
this.NumCopies = numCopies;
}
// Methods
public override void Display()
{
Console.WriteLine( " Book ------ " );
Console.WriteLine( " Author: {0}", author );
Console.WriteLine( " Title: {0}", title );
Console.WriteLine( " # Copies: {0}", NumCopies );
}
}
// "ConcreteComponent"
class Video : LibraryItem
{
// Fields
private string director;
private string title;
private int playTime;
// Constructor
public Video( string director, string title,
int numCopies, int playTime )
{
this.director = director;
this.title = title;
this.NumCopies = numCopies;
this.playTime = playTime;
}
// Methods
public override void Display()
{
Console.WriteLine( " Video ----- " );
Console.WriteLine( " Director: {0}", director );
Console.WriteLine( " Title: {0}", title );
Console.WriteLine( " # Copies: {0}", NumCopies );
Console.WriteLine( " Playtime: {0}", playTime );
}
}
// "Decorator"
abstract class Decorator : LibraryItem
{
// Fields
protected LibraryItem libraryItem;
// Constructors
public Decorator ( LibraryItem libraryItem )
{ this.libraryItem = libraryItem; }
// Methods
public override void Display()
{ libraryItem.Display(); }
}
// "ConcreteDecorator"
class Borrowable : Decorator
{
// Fields
protected ArrayList borrowers = new ArrayList();
// Constructors
public Borrowable( LibraryItem libraryItem )
: base( libraryItem ) {}
// Methods
public void BorrowItem( string name )
{
borrowers.Add( name );
libraryItem.NumCopies--;
}
public void ReturnItem( string name )
{
borrowers.Remove( name );
libraryItem.NumCopies++;
}
public override void Display()
{
base.Display();
foreach( string borrower in borrowers )
Console.WriteLine( " borrower: {0}", borrower );
}
}
/// <summary>
/// DecoratorApp test
/// </summary>
public class DecoratorApp
{
public static void Main( string[] args )
{
// Create book and video and display
Book book = new Book( "Schnell", "My Home", 10 );
Video video = new Video( "Spielberg",
"Schindler's list", 23, 60 );
book.Display();
video.Display();
// Make video borrowable, then borrow and display
Console.WriteLine( " Video made borrowable:" );
Borrowable borrowvideo = new Borrowable( video );
borrowvideo.BorrowItem( "Cindy Lopez" );
borrowvideo.BorrowItem( "Samuel King" );
borrowvideo.Display();
}
}
六、 使用装饰模式的优点和缺点
使用装饰模式主要有以下的优点:
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
- 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错。
使用装饰模式主要有以下的缺点:
由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
七、 模式实现的讨论
大多数情况下,装饰模式的实现都比上面定义中给出的示意性实现要简单。对模式进行简化时需要注意以下的情况:
(1)一个装饰类的接口必须与被装饰类的接口相容。
(2)尽量保持Component作为一个"轻"类,不要把太多的逻辑和状态放在Component类里。
(3)如果只有一个ConcreteComponent类而没有抽象的Component类(接口),那么Decorator类经常可以是ConcreteComponent的一个子类。如下图所示:
(4)如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。
八、 透明性的要求
透明的装饰模式
装饰模式通常要求针对抽象编程。装饰模式对客户端的透明性要求程序不要声明一个ConcreteDecorator类型的变量,而应当声明一个Component类型的变量。换言之,下面的做法是对的:
Component c1 = new ConcreteDecorator1(c);
Component c2 = new ConcreteDecorator(c1);
而下面的做法是不对的:
这就是前面所说的,装饰模式对客户端是完全透明的含义。
用孙悟空的例子来说,必须永远把孙悟空的所有变化都当成孙悟空来对待,而如果把老孙变成的雀儿当成雀儿,而不是老孙,那就被老孙骗了,而这是不应当发生的。
下面的做法是不对的:
雀儿 bird = new 雀儿 (c);
半透明的装饰模式
然而,纯粹的装饰模式很难找到。装饰模式的用意是在不改变接口的前提下,增强所考虑的类的性能。在增强性能的时候,往往需要建立新的公开的方法。即便是在孙大圣的系统里,也需要新的方法。比如齐天大圣类并没有飞行的能力,而雀儿有。这就意味着雀儿应当有一个新的fly()方法。
这就导致了大多数的装饰模式的实现都是"半透明"(semi-transparent)的,而不是完全"透明"的。换言之,允许装饰模式改变接口,增加新的方法。即声明ConcreteDecorator类型的变量,从而可以调用ConcreteDecorator类中才有的方法:
雀儿 bird = new 雀儿(c);
bird.fly();
齐天大圣接口根本没有fly()这个方法,而雀儿接口里有这个方法。
九、 装饰模式在.NET中的应用
.net中存在如下类模型:
下面的代码段用来将XmlDocument的内容格式输出。我们可以体会Decorator模式在这里所起的作用。
MemoryStream ms = new MemoryStream();
// 用XmlTextWriter对内存流 ms 进行装饰
// 此处使用了半透明的装饰模式
XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF8);
xtw.Formatting = Formatting.Indented;
// 对装饰xtw的操作会转而操作本体-内存流ms
xmlDoc.Save(xtw);
byte [] buf = ms.ToArray();
txtResult.Text = Encoding.UTF8.GetString(buf, 0 ,buf.Length);
xtw.Close();
参考文献:
阎宏,《Java与模式》,电子工业出版社
[美]James W. Cooper,《C#设计模式》,电子工业出版社
[美]Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社
[美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
[美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社
.NET设计模式(10):装饰模式(Decorator Pattern)
装饰模式(Decorator Pattern)
——.NET设计模式系列之十
Terrylee,2006年3月
概述
在软件系统中,有时候我们会使用继承来扩展对象的功能,但是由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?这就是本文要讲的Decorator模式。
意图
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。[GOF 《设计模式》]
结构图
图1 Decorator模式结构图
生活中的例子
装饰模式动态地给一个对象添加额外的职责。不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
图2 使用有画框的画作为例子的装饰模式对象图
装饰模式解说
在软件开发中,经常会遇到动态地为一个对象而不是整个类增加一些功能的问题,还是以我惯用的记录日志的例子来说明吧(也许在Decorator模式里面用这个例子不是特别合适)。现在要求我们开发的记录日志的组件,除了要支持数据库记录DatabaseLog和文本文件记录TextFileLog两种方式外,我们还需要在不同的应用环境中增加一些额外的功能,比如需要记录日志信息的错误严重级别,需要记录日志信息的优先级别,还有日志信息的扩展属性等功能。在这里,如果我们不去考虑设计模式,解决问题的方法其实很简单,可以通过继承机制去实现,日志类结构图如下:
图3
实现代码如下:
public abstractclassLog
{
public abstract void Write(string log);
}
public classDatabaseLog :Log
{
public override void Write(string log)
{
//......记录到数据库中
}
}
public classTextFileLog :Log
{
public override void Write(string log)
{
//......记录到文本文件中
}
}
需要记录日志信息的错误严重级别功能和记录日志信息优先级别的功能,只要在原来子类DatabaseLog和TextFileLog的基础上再生成子类即可,同时需要引进两个新的接口IError和I Priority,类结构图如下:
图4
实现代码如下:
public interfaceIError
{
void SetError();
}
public interfaceIPriority
{
void SetPriority();
}
public classDBErrorLog :DatabaseLog,IError
{
public override void Write(string log)
{
base.Write(log);
}
public void SetError()
{
//......功能扩展,实现了记录错误严重级别
}
}
public classDBPriorityLog :DatabaseLog,IPriority
{
public override void Write(string log)
{
base.Write(log);
}
public void SetPriority()
{
//......功能扩展,实现了记录优先级别
}
}
public classTFErrorLog :TextFileLog,IError
{
public override void Write(string log)
{
base.Write(log);
}
public void SetError()
{
//......功能扩展,实现了记录错误严重级别
}
}
public classTFPriorityLog :TextFileLog,IPriority
{
public override void Write(string log)
{
base.Write(log);
}
public void SetPriority()
{
//......功能扩展,实现了记录优先级别
}
}
此时可以看到,如果需要相应的功能,直接使用这些子类就可以了。这里我们采用了类的继承方式来解决了对象功能的扩展问题,这种方式是可以达到我们预期的目的。然而,它却带来了一系列的问题。首先,前面的分析只是进行了一种功能的扩展,如果既需要记录错误严重级别,又需要记录优先级时,子类就需要进行接口的多重继承,这在某些情况下会违反类的单一职责原则,注意下图中的蓝色区域:
图5
实现代码:
public classDBEPLog :DatabaseLog,IError,IPriority
{
public override void Write(string log)
{
SetError();
SetPriority();
base.Write(log);
}
public void SetError()
{
//......功能扩展,实现了记录错误严重级别
}
public void SetPriority()
{
//......功能扩展,实现了记录优先级别
}
}
public classTFEPLog :DatabaseLog,IError,IPriority
{
public override void Write(string log)
{
SetError();
SetPriority();
base.Write(log);
}
public void SetError()
{
//......功能扩展,实现了记录错误严重级别
}
public void SetPriority()
{
//......功能扩展,实现了记录优先级别
}
}
其次,随着以后扩展功能的增多,子类会迅速的膨胀,可以看到,子类的出现其实是DatabaseLog和TextFileLog两个子类与新增加的接口的一种排列组合关系,所以类结构会变得很复杂而难以维护,正如象李建忠老师说的那样“子类复子类,子类何其多”;最后,这种方式的扩展是一种静态的扩展方式,并没有能够真正实现扩展功能的动态添加,客户程序不能选择添加扩展功能的方式和时机。
现在又该是Decorator模式出场的时候了,解决方案是把Log对象嵌入到另一个对象中,由这个对象来扩展功能。首先我们要定义一个抽象的包装类LogWrapper,让它继承于Log类,结构图如下:
图6
实现代码如下:
public abstractclassLogWrapper :Log
{
private Log _log;
public LogWrapper(Log log)
{
_log = log;
}
public override void Write(string log)
{
_log.Write(log);
}
}
现在对于每个扩展的功能,都增加一个包装类的子类,让它们来实现具体的扩展功能,如下图中绿色的区域:
图7
实现代码如下:
public classLogErrorWrapper :LogWrapper
{
public LogErrorWrapper(Log _log)
:base(_log)
{
}
public override void Write(string log)
{
SetError(); //......功能扩展
base.Write(log);
}
public void SetError()
{
//......实现了记录错误严重级别
}
}
public classLogPriorityWrapper :LogWrapper
{
public LogPriorityWrapper(Log _log)
: base(_log)
{
}
public override void Write(string log)
{
SetPriority(); //......功能扩展
base.Write(log);
}
public void SetPriority()
{
//......实现了记录优先级别
}
}
到这里,LogErrorWrapper类和LogPriorityWrapper类真正实现了对错误严重级别和优先级别的功能的扩展。我们来看一下客户程序如何去调用它:
public classProgram
{
public static void Main(string[] args)
{
Log log = new DatabaseLog();
LogWrapper lew1 = new LogErrorWrapper(log);
//扩展了记录错误严重级别
lew1.Write("Log Message");
LogPriorityWrapper lpw1 =newLogPriorityWrapper(log);
//扩展了记录优先级别
lpw1.Write("Log Message");
LogWrapper lew2 = new LogErrorWrapper(log);
LogPriorityWrapper lpw2 =newLogPriorityWrapper(lew2);//这里是lew2
//同时扩展了错误严重级别和优先级别
lpw2.Write("Log Message");
}
}
注意在上面程序中的第三段装饰才真正体现出了Decorator模式的精妙所在,这里总共包装了两次:第一次对log对象进行错误严重级别的装饰,变成了lew2对象,第二次再对lew2对象进行装饰,于是变成了lpw2对象,此时的lpw2对象同时扩展了错误严重级别和优先级别的功能。也就是说我们需要哪些功能,就可以这样继续包装下去。到这里也许有人会说LogPriorityWrapper类的构造函数接收的是一个Log对象,为什么这里可以传入LogErrorWrapper对象呢?通过类结构图就能发现,LogErrorWrapper类其实也是Log类的一个子类。
我们分析一下这样会带来什么好处?首先对于扩展功能已经实现了真正的动态增加,只在需要某种功能的时候才进行包装;其次,如果再出现一种新的扩展功能,只需要增加一个对应的包装子类(注意:这一点任何时候都是避免不了的),而无需再进行很多子类的继承,不会出现子类的膨胀,同时Decorator模式也很好的符合了面向对象设计原则中的“优先使用对象组合而非继承”和“开放-封闭”原则。
.NET中的装饰模式
1..NET中Decorator模式一个典型的运用就是关于Stream,它存在着如下的类结构:
图8
可以看到, BufferedStream和CryptoStream其实就是两个包装类,这里的Decorator模式省略了抽象装饰角色(Decorator),示例代码如下:
class Program
{
public static void Main(string[] args)
{
MemoryStream ms =
new MemoryStream(new byte[] { 100,456,864,222,567});
//扩展了缓冲的功能
BufferedStream buff = new BufferedStream(ms);
//扩展了缓冲,加密的功能
CryptoStream crypto = new CryptoStream(buff);
}
}
通过反编译,可以看到BufferedStream类的代码(只列出部分),它是继承于Stream类:
public sealedclassBufferedStream : Stream
{
// Methods
private BufferedStream();
public BufferedStream(Stream stream);
public BufferedStream(Stream stream,int bufferSize);
// Fields
private int _bufferSize;
private Stream _s;
}
2.在Enterprise Library中的DAAB中有一个DbCommandWrapper的包装类,它实现了对IDbCommand类的包装并提供了参数处理的功能。结构图如下:
图9
示意性代码如下:
public abstractclassDBCommandWrapper :MarshalByRefObject,IDisposable
{
}
public classSqlCommandWrapper :DBCommandWrapper
{
}
public classOracleCommandWrapper :DBCommandWrapper
{
}
效果及实现要点
1.Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明,换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。
2.Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。
3.Decortor模式并非解决“多子类衍生的多继承”问题,Decorator模式的应用要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
4.对于Decorator模式在实际中的运用可以很灵活。如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。
图10
如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。
图11
5.Decorator模式的优点是提供了比继承更加灵活的扩展,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。
6.由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
适用性
在以下情况下应当使用装饰模式:
1.需要扩展一个类的功能,或给一个类增加附加责任。
2.需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
3.需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。
总结
Decorator模式采用对象组合而非继承的手法,实现了在运行时动态的扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。同时它很好地符合面向对象设计原则中“优先使用对象组合而非继承”和“开放-封闭”原则。
参考资料
阎宏,《Java与模式》,电子工业出版社
James W. Cooper,《C#设计模式》,电子工业出版社
Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社
MSDN WebCast 《C#面向对象设计模式纵横谈(10) Decorator装饰模式(结构型模式)》
出处: http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
测试输出
using System;
using System.Collections.Generic;
using System.Text;
namespace DecoratorPatternTest
{
// Component
public abstract class Log
{
public Log()
{
Console.WriteLine("[Log]:Constructor.");
}
public abstract void WriteLog(string log);
}
//Concrete component
public class DatabaseLog : Log
{
public DatabaseLog()
{
Console.WriteLine("[DatabaseLog]:Constructor.");
}
public override void WriteLog(string log)
{
Console.WriteLine("[DatabaseLog]:" + log);
}
}
//Concrete component
public class TextLog : Log
{
public TextLog()
{
Console.WriteLine("[TextLog]:Constructor.");
}
public override void WriteLog(string log)
{
Console.WriteLine("[TextLog]:" + log);
}
}
//Decorator
public abstract class LogWrapper : Log
{
private Log _log;
public LogWrapper(Log log)
{
this._log = log;
Console.WriteLine("[LogWrapper]:Constructor.");
}
public override void WriteLog(string log)
{
Console.WriteLine("[LogWrapper]:" + log); //9,此行屏蔽
this._log.WriteLog(log);
}
}
//Concrete Decorator
public class LogErrorWrapper : LogWrapper
{
public LogErrorWrapper(Log log)
: base(log)
{
Console.WriteLine("[LogErrorWrapper]:Constructor.");
}
public override void WriteLog(string log)
{
Console.WriteLine("[LogErrorWrapper.Error]:" + log);
base.WriteLog(log);
}
}
//Concrete Decorator
public class LogPriorityWrapper:LogWrapper
{
public LogPriorityWrapper(Log log)
: base(log)
{
Console.WriteLine("[LogPriorityWrapper]:Constructor.");
}
public override void WriteLog(string log)
{
Console.WriteLine("[LogPriorityWrapper.Priority]:" + log);
base.WriteLog(log);
}
}
class Program
{
static void Main(string[] args)
{
Log log = new DatabaseLog(); //1
LogWrapper lew1 = new LogErrorWrapper(log); //2
lew1.WriteLog("Log Message Error."); //3
LogPriorityWrapper lpw1 = new LogPriorityWrapper(log); //4
lpw1.WriteLog("Log Message Priority."); //5
LogWrapper lew2 = new LogErrorWrapper(log); //6
LogPriorityWrapper lpw2 = new LogPriorityWrapper(lew2); //7
lpw2.WriteLog("Log Message Error and Priority."); //8
Console.Read();
}
}
}
输出情况:(每行对应的输出情况)
//9,屏蔽下的输出,(红线部分,通过装饰,扩展后的新功能)
乐在其中设计模式(C#) - 装饰模式(Decorator Pattern)
作者: webabcd
介绍
动态地给一个对象添加一些额外的职责。就扩展功能而言,它比生成子类方式更为灵活。
示例
有一个Message实体类,某个对象对它的操作有Insert()和Get()方法,现在扩展这个对象的功能。
MessageModel
using System.Collections.Generic;
using System.Text;
namespace Pattern.Decorator
{
/// <summary>
/// Message实体类
/// </summary>
public class MessageModel
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="msg">Message内容</param>
/// <param name="pt">Message发布时间</param>
public MessageModel(string msg, DateTime pt)
{
this._message = msg;
this._publishTime = pt;
}
private string _message;
/// <summary>
/// Message内容
/// </summary>
public string Message
{
get { return _message; }
set { _message = value; }
}
private DateTime _publishTime;
/// <summary>
/// Message发布时间
/// </summary>
public DateTime PublishTime
{
get { return _publishTime; }
set { _publishTime = value; }
}
}
}
AbstractMessage
using System.Collections.Generic;
using System.Text;
namespace Pattern.Decorator
{
/// <summary>
/// 操作Message的抽象构件(Component)
/// </summary>
public abstract class AbstractMessage
{
/// <summary>
/// 获取Message
/// </summary>
/// <returns></returns>
public abstract List<MessageModel> Get();
/// <summary>
/// 插入Message
/// </summary>
/// <param name="mm">Message实体对象</param>
/// <returns></returns>
public abstract bool Insert(MessageModel mm);
}
}
SqlMessage
using System.Collections.Generic;
using System.Text;
namespace Pattern.Decorator
{
/// <summary>
/// Sql方式操作Message(ConcreteComponent)
/// </summary>
public class SqlMessage : AbstractMessage
{
/// <summary>
/// 获取Message
/// </summary>
/// <returns></returns>
public override List<MessageModel> Get()
{
List<MessageModel> l = new List<MessageModel>();
l.Add(new MessageModel("SQL方式获取Message", DateTime.Now));
return l;
}
/// <summary>
/// 插入Message
/// </summary>
/// <param name="mm">Message实体对象</param>
/// <returns></returns>
public override bool Insert(MessageModel mm)
{
// 代码略
return true;
}
}
}
XmlMessage
using System.Collections.Generic;
using System.Text;
namespace Pattern.Decorator
{
/// <summary>
/// Xml方式操作Message(ConcreteComponent)
/// </summary>
public class XmlMessage : AbstractMessage
{
/// <summary>
/// 获取Message
/// </summary>
/// <returns></returns>
public override List<MessageModel> Get()
{
List<MessageModel> l = new List<MessageModel>();
l.Add(new MessageModel("XML方式获取Message", DateTime.Now));
return l;
}
/// <summary>
/// 插入Message
/// </summary>
/// <param name="mm">Message实体对象</param>
/// <returns></returns>
public override bool Insert(MessageModel mm)
{
// 代码略
return true;
}
}
}
AbstractMessageWrapper
using System.Collections.Generic;
using System.Text;
namespace Pattern.Decorator
{
/// <summary>
/// 装饰AbstractMessage(Decorator)
/// </summary>
public abstract class AbstractMessageWrapper : AbstractMessage
{
private AbstractMessage _abstractMessage;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="abstractMessage">AbstractMessage</param>
public AbstractMessageWrapper(AbstractMessage abstractMessage)
{
this._abstractMessage = abstractMessage;
}
/// <summary>
/// 获取Message
/// </summary>
/// <returns></returns>
public override List<MessageModel> Get()
{
return _abstractMessage.Get();
}
/// <summary>
/// 插入Message
/// </summary>
/// <param name="mm">Message实体对象</param>
/// <returns></returns>
public override bool Insert(MessageModel mm)
{
return _abstractMessage.Insert(mm);
}
}
}
CheckUserWrapper
using System.Collections.Generic;
using System.Text;
namespace Pattern.Decorator
{
/// <summary>
/// 扩展出用户验证的功能(ConcreteDecorator)
/// </summary>
public class CheckUserWrapper : AbstractMessageWrapper
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="abstractMessage">AbstractMessage</param>
public CheckUserWrapper(AbstractMessage abstractMessage)
: base(abstractMessage)
{
}
/// <summary>
/// 获取Message
/// </summary>
/// <returns></returns>
public override List<MessageModel> Get()
{
List<MessageModel> l = base.Get();
foreach (MessageModel mm in l)
{
mm.Message += "(经过用户验证)";
}
return l;
}
/// <summary>
/// 插入Message
/// </summary>
/// <param name="mm">Message实体对象</param>
/// <returns></returns>
public override bool Insert(MessageModel mm)
{
// 在这里扩展功能
return base.Insert(mm);
}
}
}
CheckInputWrapper
using System.Collections.Generic;
using System.Text;
namespace Pattern.Decorator
{
/// <summary>
/// 扩展出输入验证的功能(ConcreteDecorator)
/// </summary>
public class CheckInputWrapper : AbstractMessageWrapper
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="abstractMessage">AbstractMessage</param>
public CheckInputWrapper(AbstractMessage abstractMessage)
: base(abstractMessage)
{
}
/// <summary>
/// 获取Message
/// </summary>
/// <returns></returns>
public override List<MessageModel> Get()
{
List<MessageModel> l = base.Get();
foreach (MessageModel mm in l)
{
mm.Message += "(经过输入验证)";
}
return l;
}
/// <summary>
/// 插入Message
/// </summary>
/// <param name="mm">Message实体对象</param>
/// <returns></returns>
public override bool Insert(MessageModel mm)
{
// 在这里扩展功能
return base.Insert(mm);
}
}
}
Test
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Pattern.Decorator;
public partial class Decorator : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
AbstractMessage message = new SqlMessage();
AbstractMessageWrapper amr = new CheckUserWrapper(message);
Response.Write(amr.Get()[0].Message + " " + amr.Get()[0].PublishTime.ToString());
Response.Write("<br />");
AbstractMessageWrapper amr2 = new CheckInputWrapper(message);
Response.Write(amr2.Get()[0].Message + " " + amr2.Get()[0].PublishTime.ToString());
Response.Write("<br />");
AbstractMessageWrapper amr3 = new CheckUserWrapper(message);
AbstractMessageWrapper amr4 = new CheckInputWrapper(amr3);
Response.Write(amr4.Get()[0].Message + " " + amr4.Get()[0].PublishTime.ToString());
Response.Write("<br />");
}
}
运行结果
SQL方式获取Message(经过用户验证) 2007-5-13 19:34:01
SQL方式获取Message(经过输入验证) 2007-5-13 19:34:01
SQL方式获取Message(经过用户验证)(经过输入验证) 2007-5-13 19:34:01
参考
http://www.dofactory.com/Patterns/PatternDecorator.aspx
OK
[源码下载]