設計模式學習筆記十五:裝飾模式(Decorator Pattern)
1.概述
將表現與邏輯分離,是應用設計的一重要原則,在WEB應用中顯得尤為重要,因為使用者對介面形式的要求是易變的,並且是非常苛刻的。如果應用邏輯與顯示糾纏在一起,就會導致對介面上既是很小的一點改動,都會牽扯到邏輯的變化。在這種情況下,我們可以繼承來擴充套件物件的功能,但是由於繼承為型別引入的靜態特質,使得這種擴充套件方式缺乏靈活性;並且隨著子類的增多(擴充套件功能的增多),各種子類的組合(擴充套件功能的組合)會導致更多子類的膨脹。如何使“物件功能的擴充套件”能夠根據需要來動態地實現?同時避免“擴充套件功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴充套件變化”所導致的影響將為最低?這就是裝飾模式。
裝飾模式:動態地給一個物件新增一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。說白了,就是在不改變物件的前提下,動態的增加其功能,即我們不希望改變原有的類,或採用建立子類的方法來增加功能的時候,這種情況下我們要採用裝飾模式。裝飾模式的結構圖如下:
模式中的參與者如下: (1).Component:定義一個物件介面,可以動態新增這些物件的功能; (2).ConcreteComponent:定義一個物件,可以為其新增一些功能; (3).Decorator:維持一個對Component物件的引用,並定義與Component介面一致的介面; (4).ConcreteDecorator:為元件新增功能。 下面是基本的實現程式碼:publicabstractclass Component
{
publicabstractvoid Operation();
}
publicclass ConcreteComponent : Component
{
publicoverridevoid Operation()
{
Console.WriteLine("具體物件的操作");
}
}
publicabstractclass Decorator : Component
{
publicvoid SetComponent(Component component)
{
this.component = component;
}
publicoverridevoid Operation()
{
if (component !=null)
{
component.Operation();
}
}
}
publicclass ConcreteDecoratorA : Decorator
{
privatestring addedState;
publicoverridevoid Operation()
{
base.Operation();
addedState ="New State";
Console.WriteLine("具體裝飾物件A的操作");
}
}
publicclass ConcreteDecoratorB : Decorator
{
publicoverridevoid Operation()
{
base.Operation();
AddedBehavior();
Console.WriteLine("具體裝飾物件B的操作");
}
privatevoid AddedBehavior()
{
}
} 呼叫程式碼:
ConcreteComponent c =new ConcreteComponent();
ConcreteDecoratorA d1 =new ConcreteDecoratorA();
ConcreteDecoratorB d2 =new ConcreteDecoratorB();
d1.SetComponent(c);
d2.SetComponent(d1);
d2.Operation();
修飾一個物件後,其介面不應該發生變化;否則這個物件不能被原有呼叫者使用,修飾失去了意義。由此引出了裝飾模式最重要的一點,即裝飾模者和被裝飾者具有相同的介面。說白了,即動態增加的功能不能不應該破壞已有的介面。
2.例項
1.資料庫操作記錄
當我們執行某種操作時,希望記錄日誌。日誌實際上是與實際操作無關的輔助行為,需要執行的操作也無需知道如何記錄日誌。我們還希望可以改變記錄的方式,如選擇在資料庫,或者一個XML檔案中記錄日誌,或者不做記錄。例如我們在簡單工廠中的資料訪問,我們現在希望給資料庫訪問增加操作記錄,我們可以通過引進裝飾模式完成這個功能,我們增加一個clsAbstractDB的子類LOGSDB,由於介面相同,因此LOGDB可以與其他資料庫連線物件一樣使用,不同的是,LOGDB並不實際操作資料庫,他有一個指向clsAbstractDB的引用,通過這個具體的引用,LOGDB執行實際操作。結構圖如下:
LogDB是這麼寫的:
publicclass LOGDB : clsAbstractDB
{
private clsAbstractDB myDB;
public clsAbstractDB DB
{
get{ return myDB; }
set{ myDB = value; }
}
publicoverridevoid Open()
{
myDB.Open();
Record("Open");
}
publicoverride DataTable ExecSQL(string strSQL)
{
DataTable dt = myDB.ExecSQL(strSQL);
Record("ExecSQL: "+ strSQL);
return dt;
}
//新方法 記錄日誌
privatevoid Record(string strRecord)
{
//記錄日誌
}
}
2.經典的BufferStream-.NET中的裝飾模式
BufferStream是經典的裝飾模式。BufferStream的作用是為另一流上的讀寫操作新增一個緩衝層,從而提高讀取和寫入效能。其結構如下:
由於在.NET中已經實現了框架,我們只看怎麼使用:publicvoid BufferedStreamMethod(string path)
{
FileStream fs = File.Create(path);
Stream bs =new BufferedStream(fs);
AddText(bs, "Hello!");
}
publicvoid AddText(Stream fs, string value)
{
byte[] info =new UTF8Encoding(true).GetBytes(value);
fs.Write(info, 0, info.Length);
}
3.總結
1.適用性
在以下情況下應當使用裝飾模式:1.需要擴充套件一個類的功能,或給一個類增加附加責任;2.需要動態地給一個物件增加功能,這些功能可以再動態地撤銷;3.需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關係變得不現實。
2.實現要點
讓裝飾角色還繼承抽象構件角色也是裝飾模式最大的特點,目的就是給抽象構件增加職責,對外表現為裝飾後的構件;
讓裝飾角色擁有構件角色例項的目的就是讓構件能被多個裝飾物件來裝飾;
在具體應用中可以靈活一點,不一定要有抽象構件和裝飾角色。但是,裝飾物件繼承裝飾物件並且擁有它例項的兩大特點需要體現;
透明裝飾一般通過在基類方法前後進行擴充實現,半透明裝飾一般通過新的介面實現。
參考資料:
大話設計模式
.net與設計模式