意图
intent
:动态地给一个对象添加一些额外的职责。就增加功能来说,
Decorator
模式相比生成子类更为灵活。
适用性:1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。2)处理那些可以撤消的职责。3)当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
Definition:Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. 动态的给一个对象添加额外的职责。装饰者使子类扩展功能很灵活的改变。
Participants:The classes and/or objects participating in this pattern are:
Component (LibraryItem)
defines the interface for objects that can have responsibilities added to them dynamically.
ConcreteComponent (Book, Video)
defines an object to which additional responsibilities can be attached.
Decorator (Decorator)
maintains a reference to a Component object and defines an interface that conforms to Component's interface.
ConcreteDecorator (Borrowable)
adds responsibilities to the component.
Component是一个abstract类,提供了一个接口。ConcreteComponent类继承自Component,覆盖了Component的operation函数。Decorator也是一个abstract类,也继承自Component。这部分就类似于Composite设计模式了。Decorator中也有一个component的成员变量。接下来就是两个ConcreteDecorator了,他们非常方便地扩展了ConcreteComponent的功能。ConcreteDecorator是ConcreteComponent的decorator,而ConcreteComponent则是ConcreteDecorator的decoratee。另外,从UML图我们可以看出有两种decorator,一种是添加行为,另一种是添加state。
我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机。Decorator提供了“即插即用”的方法,在运行期间决定何时增加何种功能。
下例中的c是decoratee, d1是decorator;d2是d1的decorator。Decorator这个设计模式还是有点搞得,比composite要来得复杂。所以建议先看composite,再看decorator。Have fun & good luck。
Sample code in C#
This structural code demonstrates the Decorator pattern which dynamically adds extra functionality to an existing object.
// Decorator pattern -- Structural example
|
using System; namespace DoFactory.GangOfFour.Decorator.Structural { // MainApp test application class MainApp { static void Main() { // 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(); // Wait for user Console.Read(); } } // "Component" abstract class Component { public abstract void Operation(); } // "ConcreteComponent" class ConcreteComponent : Component { public override void Operation() { Console.WriteLine("ConcreteComponent.Operation()"); } } // "Decorator" abstract class Decorator : Component { protected Component component; public void SetComponent(Component component) { this.component = component; } public override void Operation() { if (component != null) { component.Operation(); } } } // "ConcreteDecoratorA" class ConcreteDecoratorA : Decorator { private string addedState; public override void Operation() { base.Operation(); addedState = "New State"; Console.WriteLine("ConcreteDecoratorA.Operation()"); } } // "ConcreteDecoratorB" class ConcreteDecoratorB : Decorator { public override void Operation() { base.Operation(); AddedBehavior(); Console.WriteLine("ConcreteDecoratorB.Operation()"); } void AddedBehavior() { } } }
|
Output
ConcreteComponent.Operation()
ConcreteDecoratorA.Operation()
ConcreteDecoratorB.Operation()
This real-world code demonstrates the Decorator pattern in which 'borrowable' functionality is added to existing library items (books and videos).
// Decorator pattern -- Real World example
|
using System; using System.Collections; namespace DoFactory.GangOfFour.Decorator.RealWorld { // MainApp test application class MainApp { static void Main() { // Create book Book book = new Book ("Worley", "Inside ASP.NET", 10); book.Display(); // Create video Video video = new Video ("Spielberg", "Jaws", 23, 92); video.Display(); // Make video borrowable, then borrow and display Console.WriteLine("/nMaking video borrowable:"); Borrowable borrowvideo = new Borrowable(video); borrowvideo.BorrowItem("Customer #1"); borrowvideo.BorrowItem("Customer #2"); borrowvideo.Display(); // Wait for user Console.Read(); } } // "Component" abstract class LibraryItem { private int numCopies; // Property public int NumCopies { get{ return numCopies; } set{ numCopies = value; } } public abstract void Display(); } // "ConcreteComponent" class Book : LibraryItem { private string author; private string title; // Constructor public Book(string author,string title,int numCopies) { this.author = author; this.title = title; this.NumCopies = numCopies; } public override void Display() { Console.WriteLine("/nBook ------ "); Console.WriteLine(" Author: {0}", author); Console.WriteLine(" Title: {0}", title); Console.WriteLine(" # Copies: {0}", NumCopies); } } // "ConcreteComponent" class Video : LibraryItem { 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; } public override void Display() { Console.WriteLine("/nVideo ----- "); Console.WriteLine(" Director: {0}", director); Console.WriteLine(" Title: {0}", title); Console.WriteLine(" # Copies: {0}", NumCopies); Console.WriteLine(" Playtime: {0}/n", playTime); } } // "Decorator" abstract class Decorator : LibraryItem { protected LibraryItem libraryItem; // Constructor public Decorator(LibraryItem libraryItem) { this.libraryItem = libraryItem; } public override void Display() { libraryItem.Display(); } } // "ConcreteDecorator" class Borrowable : Decorator { protected ArrayList borrowers = new ArrayList(); // Constructor public Borrowable(LibraryItem libraryItem) : base(libraryItem) { } 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: " + borrower); } } } }
|
Output
Book ------
Author: Worley
Title: Inside ASP.NET
# Copies: 10
Video -----
Director: Spielberg
Title: Jaws
# Copies: 23
Playtime: 92
Making video borrowable:
Video -----
Director: Spielberg
Title: Jaws
# Copies: 21
Playtime: 92
borrower: Customer #1
borrower: Customer #2