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

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

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

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


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

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

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

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 日日噜噜夜夜狠视频免费 | 国产精品免费视频播放 | 免费精品精品国产欧美在线 | 久久视频这里只有精品 | 欧美精品福利视频 | 久草视频在线资源 | 看日本黄色录像 | 欧美毛片xxxx | 亚洲免费小视频 | a4yy私人毛片 | 奇米777视频二区中文字幕 | 99久久免费观看 | 国产一区二区高清在线 | 五月婷综合 | 色综合激情 | 国产色婷婷视频在线观看 | 久热re国产手机在线观看 | 国产亚洲精品一区999 | 午夜视频在线网站 | 青青青国产高清免费视频 | 精品中文字幕在线观看 | 国产一在线精品一区在线观看 | 国产日韩精品欧美在线ccc | 一级毛片成人免费看a | 日日夜夜免费精品 | 亚洲成人毛片 | 成 人 免费 黄 色 视频 | 奇米影视四色7777 | 亚洲精品综合 | 久久综合噜噜激激的五月天 | 尹人香蕉久久99天天 | 国产亚洲精品视频中文字幕 | 国产大毛片 | 俄罗斯一级在线播放 | 人成午夜欧美大片免费视频 | 亚洲综合春色另类久久 | 免费观看男女羞羞的视频网站 | 在线播放精品一区二区啪视频 | 玖玖99视频 | se01国产短视频在线观看 | 欧美色99|