亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

(第Ⅲ部分 結(jié)構(gòu)型模式篇) 第9章 裝飾模式(Deco

系統(tǒng) 2550 0
——.NET設計模式系列之十
Terrylee , 2006 3
概述
在軟件系統(tǒng)中,有時候我們會使用繼承來擴展對象的功能,但是由于繼承為類型引入的靜態(tài)特質(zhì),使得這種擴展方式缺乏靈活性;并且隨著子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹。如何使“對象功能的擴展”能夠根據(jù)需要來動態(tài)地實現(xiàn)?同時避免“擴展功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴展變化”所導致的影響將為最低?這就是本文要講的 Decorator 模式。
意圖
動態(tài)地給一個對象添加一些額外的職責。就增加功能來說, Decorator 模式相比生成子類更為靈活。 [GOF 《設計模式》 ]
結(jié)構(gòu)圖
1 Decorator 模式結(jié)構(gòu)圖
生活中的例子
裝飾模式動態(tài)地給一個對象添加額外的職責。不論一幅畫有沒有畫框都可以掛在墻上,但是通常都是有畫框的,并且實際上是畫框被掛在墻上。在掛在墻上之前,畫可以被蒙上玻璃,裝到框子里;這時畫、玻璃和畫框形成了一個物體。
2 使用有畫框的畫作為例子的裝飾模式對象圖
裝飾模式解說
在軟件開發(fā)中,經(jīng)常會遇到動態(tài)地為一個對象而不是整個類增加一些功能的問題,還是以我慣用的記錄日志的例子來說明吧(也許在 Decorator 模式里面用這個例子不是特別合適)。現(xiàn)在要求我們開發(fā)的記錄日志的組件,除了要支持數(shù)據(jù)庫記錄 DatabaseLog 和文本文件記錄 TextFileLog 兩種方式外,我們還需要在不同的應用環(huán)境中增加一些額外的功能,比如需要記錄日志信息的錯誤嚴重級別,需要記錄日志信息的優(yōu)先級別,還有日志信息的擴展屬性等功能。在這里,如果我們不去考慮設計模式,解決問題的方法其實很簡單,可以通過繼承機制去實現(xiàn),日志類結(jié)構(gòu)圖如下:
3
實現(xiàn)代碼如下:
public abstract class Log
{
public abstract void Write( string log);
}
public class DatabaseLog : Log
{
public override void Write( string log)
{
//...... 記錄到數(shù)據(jù)庫中
}
}
public class TextFileLog : Log
{
public override void Write( string log)
{
//...... 記錄到文本文件中
}
}
需要記錄日志信息的錯誤嚴重級別功能和記錄日志信息優(yōu)先級別的功能,只要在原來子類 DatabaseLog TextFileLog 的基礎(chǔ)上再生成子類即可,同時需要引進兩個新的接口 IError I Priority ,類結(jié)構(gòu)圖如下:
4
實現(xiàn)代碼如下:
public interface IError
{
void SetError();
}
public interface IPriority
{
void SetPriority();
}
public class DBErrorLog : DatabaseLog , IError
{
public override void Write( string log)
{
base .Write(log);
}
public void SetError()
{
//...... 功能擴展,實現(xiàn)了記錄錯誤嚴重級別
}
}
public class DBPriorityLog : DatabaseLog , IPriority
{
public override void Write( string log)
{
base .Write(log);
}
public void SetPriority()
{
//...... 功能擴展,實現(xiàn)了記錄優(yōu)先級別
}
}
public class TFErrorLog : TextFileLog , IError
{
public override void Write( string log)
{
base .Write(log);
}
public void SetError()
{
//...... 功能擴展,實現(xiàn)了記錄錯誤嚴重級別
}
}
public class TFPriorityLog : TextFileLog , IPriority
{
public override void Write( string log)
{
base .Write(log);
}
public void SetPriority()
{
//...... 功能擴展,實現(xiàn)了記錄優(yōu)先級別
}
}
此時可以看到,如果需要相應的功能,直接使用這些子類就可以了。這里我們采用了類的繼承方式來解決了對象功能的擴展問題,這種方式是可以達到我們預期的目的。然而,它卻帶來了一系列的問題。首先,前面的分析只是進行了一種功能的擴展,如果既需要記錄錯誤嚴重級別,又需要記錄優(yōu)先級時,子類就需要進行接口的多重繼承,這在某些情況下會違反類的單一職責原則,注意下圖中的藍色區(qū)域:
5
實現(xiàn)代碼:
public class DBEPLog : DatabaseLog , IError , IPriority
{
public override void Write( string log)
{
SetError();
SetPriority();
base .Write(log);
}
public void SetError()
{
//...... 功能擴展,實現(xiàn)了記錄錯誤嚴重級別
}
public void SetPriority()
{
//...... 功能擴展,實現(xiàn)了記錄優(yōu)先級別
}
}
public class TFEPLog : DatabaseLog , IError , IPriority
{
public override void Write( string log)
{
SetError();
SetPriority();
base .Write(log);
}
public void SetError()
{
//...... 功能擴展,實現(xiàn)了記錄錯誤嚴重級別
}
public void SetPriority()
{
//...... 功能擴展,實現(xiàn)了記錄優(yōu)先級別
}
}
其次,隨著以后擴展功能的增多,子類會迅速的膨脹,可以看到,子類的出現(xiàn)其實是 DatabaseLog TextFileLog 兩個子類與新增加的接口的一種排列組合關(guān)系,所以類結(jié)構(gòu)會變得很復雜而難以維護,正如象李建忠老師說的那樣“子類復子類,子類何其多”;最后,這種方式的擴展是一種靜態(tài)的擴展方式,并沒有能夠真正實現(xiàn)擴展功能的動態(tài)添加,客戶程序不能選擇添加擴展功能的方式和時機。
現(xiàn)在又該是 Decorator 模式出場的時候了,解決方案是把 Log 對象嵌入到另一個對象中,由這個對象來擴展功能。首先我們要定義一個抽象的包裝類 LogWrapper ,讓它繼承于 Log 類,結(jié)構(gòu)圖如下:
6
實現(xiàn)代碼如下:
public abstract class LogWrapper : Log
{
private Log _log;
public LogWrapper( Log log)
{
_log = log;
}
public override void Write( string log)
{
_log.Write(log);
}
}
現(xiàn)在對于每個擴展的功能,都增加一個包裝類的子類,讓它們來實現(xiàn)具體的擴展功能,如下圖中綠色的區(qū)域:
7
實現(xiàn)代碼如下:
public class LogErrorWrapper : LogWrapper
{
public LogErrorWrapper( Log _log)
: base (_log)
{
}
public override void Write( string log)
{
SetError(); //...... 功能擴展
base .Write(log);
}
public void SetError()
{
//...... 實現(xiàn)了記錄錯誤嚴重級別
}
}
public class LogPriorityWrapper : LogWrapper
{
public LogPriorityWrapper( Log _log)
: base (_log)
{
}
public override void Write( string log)
{
SetPriority(); //...... 功能擴展
base .Write(log);
}
public void SetPriority()
{
//...... 實現(xiàn)了記錄優(yōu)先級別
}
}
到這里, LogErrorWrapper 類和 LogPriorityWrapper 類真正實現(xiàn)了對錯誤嚴重級別和優(yōu)先級別的功能的擴展。我們來看一下客戶程序如何去調(diào)用它:
public class Program
{
public static void Main( string [] args)
{
Log log = new DatabaseLog ();
LogWrapper lew1 = new LogErrorWrapper (log);
// 擴展了記錄錯誤嚴重級別
lew1.Write( "Log Message" );
LogPriorityWrapper lpw1 = new LogPriorityWrapper (log);
// 擴展了記錄優(yōu)先級別
lpw1.Write( "Log Message" );
LogWrapper lew2 = new LogErrorWrapper (log);
LogPriorityWrapper lpw2 = new LogPriorityWrapper (lew2); // 這里是lew2
// 同時擴展了錯誤嚴重級別和優(yōu)先級別
lpw2.Write( "Log Message" );
}
}
注意在上面程序中的第三段裝飾才真正體現(xiàn)出了 Decorator 模式的精妙所在,這里總共包裝了兩次:第一次對 log 對象進行錯誤嚴重級別的裝飾,變成了 lew2 對象,第二次再對 lew2 對象進行裝飾,于是變成了 lpw2 對象,此時的 lpw2 對象同時擴展了錯誤嚴重級別和優(yōu)先級別的功能。也就是說我們需要哪些功能,就可以這樣繼續(xù)包裝下去。到這里也許有人會說 LogPriorityWrapper 類的構(gòu)造函數(shù)接收的是一個Log對象,為什么這里可以傳入LogErrorWrapper對象呢?通過類結(jié)構(gòu)圖就能發(fā)現(xiàn),LogErrorWrapper類其實也是Log類的一個子類。
我們分析一下這樣會帶來什么好處?首先對于擴展功能已經(jīng)實現(xiàn)了真正的動態(tài)增加,只在需要某種功能的時候才進行包裝;其次,如果再出現(xiàn)一種新的擴展功能,只需要增加一個對應的包裝子類(注意:這一點任何時候都是避免不了的),而無需再進行很多子類的繼承,不會出現(xiàn)子類的膨脹,同時 Decorator 模式也很好的符合了面向?qū)ο笤O計原則中的“優(yōu)先使用對象組合而非繼承”和“開放 - 封閉”原則。
.NET 中的裝飾模式
1 .NET Decorator 模式一個典型的運用就是關(guān)于 Stream ,它存在著如下的類結(jié)構(gòu):
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 sealed class BufferedStream : 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 的包裝類,它實現(xiàn)了對 IDbCommand 類的包裝并提供了參數(shù)處理的功能。結(jié)構(gòu)圖如下:
9
示意性代碼如下:
public abstract class DBCommandWrapper : MarshalByRefObject , IDisposable
{
}
public class SqlCommandWrapper : DBCommandWrapper
{
}
public class OracleCommandWrapper : DBCommandWrapper
{
}
效果及實現(xiàn)要點
1 .Component類在Decorator模式中充當抽象接口的角色,不應該去實現(xiàn)具體的行為。而且Decorator類對于Component類應該透明,換言之Component類無需知道Decorator類,Decorator類是從外部來擴展Component類的功能。
2 .Decorator類在接口上表現(xiàn)為is-a Component的繼承關(guān)系,即Decorator類繼承了Component類所具有的接口。但在實現(xiàn)上又表現(xiàn)為has-a Component的組合關(guān)系,即Decorator類又使用了另外一個Component類。我們可以使用一個或者多個Decorator對象來“裝飾”一個Component對象,且裝飾后的對象仍然是一個Component對象。
3 .Decortor模式并非解決“多子類衍生的多繼承”問題,Decorator模式的應用要點在于解決“主體類在多個方向上的擴展功能”——是為“裝飾”的含義。
4 .對于Decorator模式在實際中的運用可以很靈活。 如果只有一個ConcreteComponent類而沒有抽象的Component類,那么Decorator類可以是ConcreteComponent的一個子類。
圖10
如果只有一個ConcreteDecorator類,那么就沒有必要建立一個單獨的Decorator類,而可以把Decorator和ConcreteDecorator的責任合并成一個類。
圖11
5 .Decorator模式的優(yōu)點是提供了比繼承更加靈活的擴展, 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創(chuàng)造出很多不同行為的組合。
6 .由于使用裝飾模式,可以比使用繼承關(guān)系需要較少數(shù)目的類。使用較少的類,當然使設計比較易于進行。但是,在另一方面,使用裝飾模式會產(chǎn)生比使用繼承關(guān)系更多的對象。更多的對象會使得查錯變得困難,特別是這些對象看上去都很相像。
適用性
在以下情況下應當使用裝飾模式:
1. 需要擴展一個類的功能,或給一個類增加附加責任。
2. 需要動態(tài)地給一個對象增加功能,這些功能可以再動態(tài)地撤銷。
3. 需要增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能,從而使繼承關(guān)系變得不現(xiàn)實。
總結(jié)
Decorator 模式采用對象組合而非繼承的手法,實現(xiàn)了在運行時動態(tài)的擴展對象功能的能力,而且可以根據(jù)需要擴展多個功能,避免了單獨使用繼承帶來的“靈活性差”和“多子類衍生問題”。同時它很好地符合面向?qū)ο笤O計原則中“優(yōu)先使用對象組合而非繼承”和“開放 - 封閉”原則。
參考資料
閻宏,《 Java 與模式》,電子工業(yè)出版社
James W. Cooper ,《 C# 設計模式》,電子工業(yè)出版社
Alan Shalloway James R. Trott ,《 Design Patterns Explained 》,中國電力出版社
MSDN WebCast C# 面向?qū)ο笤O計模式縱橫談 (10) Decorator 裝飾模式 ( 結(jié)構(gòu)型模式 )

(第Ⅲ部分 結(jié)構(gòu)型模式篇) 第9章 裝飾模式(Decorator Pattern)


更多文章、技術(shù)交流、商務合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 99久久久 | 天天碰天天摸天天操 | 亚洲精品三区 | 素人视频在线观看 | 曰本人一级毛片免费完整视频 | 欧美一级看片a免费视频 | 在线视频一区二区三区 | 色视频网站在线观看 | 免费视频网站一级人爱视频 | 四虎精品永久在线网址 | 日韩免费高清 | 天天爽夜夜爽人人爽 | 神马影院不卡不卡在线观看 | 爱搞逼综合 | 91中文字幕| 欧美成人手机在线视频 | 免费一级特黄3大片视频 | 国产精品视频久久 | 日韩天堂视频 | 精品国产一区二区三区四 | 99爱在线精品视频免费观看9 | av亚洲男人天堂 | 日韩精品特黄毛片免费看 | 一级毛片特级毛片黄毛片 | 337p欧美超大胆日本人术艺术 | 国产高清福利91成人 | 91大片| 色在线播放| 精品一区二区乱码久久乱码 | 四虎最新永久免费视频 | 精品四虎免费观看国产高清 | 四虎影视色费永久在线观看 | 99热精品成人免费观看 | 狠狠色噜噜狠狠狠狠69 | 久久久久依人综合影院 | 俄罗斯毛片视频 | 曰本亚洲欧洲色a在线 | 精品推荐 国产 | 国产高清在线精品一区免费97 | 成年女人永久免费观看片 | 欧美成人禁片在线www |