黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

(第Ⅲ部分 結構型模式篇) 第9章 裝飾模式(Deco

系統 2759 0
——.NET設計模式系列之十
Terrylee , 2006 3
概述
在軟件系統中,有時候我們會使用繼承來擴展對象的功能,但是由于繼承為類型引入的靜態特質,使得這種擴展方式缺乏靈活性;并且隨著子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹。如何使“對象功能的擴展”能夠根據需要來動態地實現?同時避免“擴展功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴展變化”所導致的影響將為最低?這就是本文要講的 Decorator 模式。
意圖
動態地給一個對象添加一些額外的職責。就增加功能來說, Decorator 模式相比生成子類更為靈活。 [GOF 《設計模式》 ]
結構圖
1 Decorator 模式結構圖
生活中的例子
裝飾模式動態地給一個對象添加額外的職責。不論一幅畫有沒有畫框都可以掛在墻上,但是通常都是有畫框的,并且實際上是畫框被掛在墻上。在掛在墻上之前,畫可以被蒙上玻璃,裝到框子里;這時畫、玻璃和畫框形成了一個物體。
2 使用有畫框的畫作為例子的裝飾模式對象圖
裝飾模式解說
在軟件開發中,經常會遇到動態地為一個對象而不是整個類增加一些功能的問題,還是以我慣用的記錄日志的例子來說明吧(也許在 Decorator 模式里面用這個例子不是特別合適)。現在要求我們開發的記錄日志的組件,除了要支持數據庫記錄 DatabaseLog 和文本文件記錄 TextFileLog 兩種方式外,我們還需要在不同的應用環境中增加一些額外的功能,比如需要記錄日志信息的錯誤嚴重級別,需要記錄日志信息的優先級別,還有日志信息的擴展屬性等功能。在這里,如果我們不去考慮設計模式,解決問題的方法其實很簡單,可以通過繼承機制去實現,日志類結構圖如下:
3
實現代碼如下:
public abstract class Log
{
public abstract void Write( string log);
}
public class DatabaseLog : Log
{
public override void Write( string log)
{
//...... 記錄到數據庫中
}
}
public class TextFileLog : Log
{
public override void Write( string log)
{
//...... 記錄到文本文件中
}
}
需要記錄日志信息的錯誤嚴重級別功能和記錄日志信息優先級別的功能,只要在原來子類 DatabaseLog TextFileLog 的基礎上再生成子類即可,同時需要引進兩個新的接口 IError I Priority ,類結構圖如下:
4
實現代碼如下:
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()
{
//...... 功能擴展,實現了記錄錯誤嚴重級別
}
}
public class DBPriorityLog : DatabaseLog , IPriority
{
public override void Write( string log)
{
base .Write(log);
}
public void SetPriority()
{
//...... 功能擴展,實現了記錄優先級別
}
}
public class TFErrorLog : TextFileLog , IError
{
public override void Write( string log)
{
base .Write(log);
}
public void SetError()
{
//...... 功能擴展,實現了記錄錯誤嚴重級別
}
}
public class TFPriorityLog : TextFileLog , IPriority
{
public override void Write( string log)
{
base .Write(log);
}
public void SetPriority()
{
//...... 功能擴展,實現了記錄優先級別
}
}
此時可以看到,如果需要相應的功能,直接使用這些子類就可以了。這里我們采用了類的繼承方式來解決了對象功能的擴展問題,這種方式是可以達到我們預期的目的。然而,它卻帶來了一系列的問題。首先,前面的分析只是進行了一種功能的擴展,如果既需要記錄錯誤嚴重級別,又需要記錄優先級時,子類就需要進行接口的多重繼承,這在某些情況下會違反類的單一職責原則,注意下圖中的藍色區域:
5
實現代碼:
public class DBEPLog : DatabaseLog , IError , IPriority
{
public override void Write( string log)
{
SetError();
SetPriority();
base .Write(log);
}
public void SetError()
{
//...... 功能擴展,實現了記錄錯誤嚴重級別
}
public void SetPriority()
{
//...... 功能擴展,實現了記錄優先級別
}
}
public class TFEPLog : 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 abstract class LogWrapper : Log
{
private Log _log;
public LogWrapper( Log log)
{
_log = log;
}
public override void Write( string log)
{
_log.Write(log);
}
}
現在對于每個擴展的功能,都增加一個包裝類的子類,讓它們來實現具體的擴展功能,如下圖中綠色的區域:
7
實現代碼如下:
public class LogErrorWrapper : LogWrapper
{
public LogErrorWrapper( Log _log)
: base (_log)
{
}
public override void Write( string log)
{
SetError(); //...... 功能擴展
base .Write(log);
}
public void SetError()
{
//...... 實現了記錄錯誤嚴重級別
}
}
public class LogPriorityWrapper : LogWrapper
{
public LogPriorityWrapper( Log _log)
: base (_log)
{
}
public override void Write( string log)
{
SetPriority(); //...... 功能擴展
base .Write(log);
}
public void SetPriority()
{
//...... 實現了記錄優先級別
}
}
到這里, LogErrorWrapper 類和 LogPriorityWrapper 類真正實現了對錯誤嚴重級別和優先級別的功能的擴展。我們來看一下客戶程序如何去調用它:
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);
// 擴展了記錄優先級別
lpw1.Write( "Log Message" );
LogWrapper lew2 = new LogErrorWrapper (log);
LogPriorityWrapper lpw2 = new LogPriorityWrapper (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 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 的包裝類,它實現了對 IDbCommand 類的包裝并提供了參數處理的功能。結構圖如下:
9
示意性代碼如下:
public abstract class DBCommandWrapper : MarshalByRefObject , IDisposable
{
}
public class SqlCommandWrapper : DBCommandWrapper
{
}
public class OracleCommandWrapper : 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 裝飾模式 ( 結構型模式 )

(第Ⅲ部分 結構型模式篇) 第9章 裝飾模式(Decorator Pattern)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

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

【本文對您有幫助就好】

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

發表我的評論
最新評論 總共0條評論