本文轉自一下博客:裝飾模式html
裝飾(Decorator)模式又名包裝(Wrapper)模式[GOF95]。裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案。編程
引言app
孫悟空有七十二般變化,他的每一種變化都給他帶來一種附加的本領。他變成魚兒時,就能夠到水裏游泳;他變成雀兒時,就能夠在天上飛行。而無論悟空怎麼變化,在二郎神眼裏,他永遠是那隻猢猻。ide
裝飾模式以對客戶透明的方式動態地給一個對象附加上更多的責任。換言之,客戶端並不會以爲對象在裝飾前和裝飾後有什麼不一樣。裝飾模式能夠在不使用創造更多子類的狀況下,將對象的功能加以擴展。函數
裝飾模式使用原來被裝飾的類的一個子類的實例,把客戶端的調用委派到被裝飾類。裝飾模式的關鍵在於這種擴展是徹底透明的。性能
在孫猴子的例子裏,老孫變成的魚兒至關於老孫的子類,這條魚兒與外界的互動要經過"委派",交給老孫的本尊,由老孫本尊採起行動。this
裝飾模式的類圖以下圖所示:spa
在裝飾模式中的各個角色有:.net
如下示例性代碼實現了裝飾模式:設計
// Decorator pattern -- Structural example 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 Decorator1( new Decorator2( new Decorator3( new ConcreteComponent() ) ) )
裝飾模式經常被稱爲包裹模式,就是由於每個具體裝飾類都將下一個具體裝飾類或者具體構件類包裹起來。
在如下狀況下應當使用裝飾模式:
該例子演示了經過裝飾模式爲圖書館的圖書與錄像帶添加"可借閱"裝飾。
// Decorator pattern -- Real World example 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 c = new ConcreteComponent(); Component c1 = new ConcreteDecorator1(c); Component c2 = new ConcreteDecorator(c1);
而下面的作法是不對的:
ConcreteComponent c = new ConcreteDecorator();
這就是前面所說的,裝飾模式對客戶端是徹底透明的含義。
用孫悟空的例子來講,必須永遠把孫悟空的全部變化都當成孫悟空來對待,而若是把老孫變成的雀兒當成雀兒,而不是老孫,那就被老孫騙了,而這是不該當發生的。
下面的作法是不對的:
大聖本尊 c = new 大聖本尊(); 雀兒 bird = new 雀兒 (c);
半透明的裝飾模式
然而,純粹的裝飾模式很難找到。裝飾模式的用意是在不改變接口的前提下,加強所考慮的類的性能。在加強性能的時候,每每須要創建新的公開的方法。即使是在孫大聖的系統裏,也須要新的方法。好比齊天大聖類並無飛行的能力,而雀兒有。這就意味着雀兒應當有一個新的fly()方法。
這就致使了大多數的裝飾模式的實現都是"半透明"(semi-transparent)的,而不是徹底"透明"的。換言之,容許裝飾模式改變接口,增長新的方法。即聲明ConcreteDecorator類型的變量,從而能夠調用ConcreteDecorator類中才有的方法:
齊天大聖 c = new 大聖本尊(); 雀兒 bird = new 雀兒(c); bird.fly();
齊天大聖接口根本沒有fly()這個方法,而雀兒接口裏有這個方法。
.net中存在以下類模型:
下面的代碼段用來將XmlDocument的內容格式輸出。咱們能夠體會Decorator模式在這裏所起的做用。
// 生成ConcreteComponent(內存流ms) 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();