Java裝飾模式
說(shuō)明:
裝飾模式是在不必改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)的擴(kuò)展一個(gè)對(duì)象的功能。它是通過(guò)創(chuàng)建一個(gè)包裝對(duì)象,也就是裝飾來(lái)包裹真實(shí)的對(duì)象。裝飾模式的特點(diǎn);
(1) 裝飾對(duì)象和真實(shí)對(duì)象有相同的接口。這樣客戶端對(duì)象就可以以和真實(shí)對(duì)象相同的方式和裝飾對(duì)象交互。
(2) 裝飾對(duì)象包含一個(gè)真實(shí)對(duì)象的索引(reference)
(3) 裝飾對(duì)象接受所有的來(lái)自客戶端的請(qǐng)求。它把這些請(qǐng)求轉(zhuǎn)發(fā)給真實(shí)的對(duì)象。
(4) 裝飾對(duì)象可以在轉(zhuǎn)發(fā)這些請(qǐng)求以前或以后增加一些附加功能。這樣就確保了在運(yùn)行時(shí),不用修改給定對(duì)象的結(jié)構(gòu)就可以在外部增加附加的功能。在面向?qū)ο蟮脑O(shè)計(jì)中,通常是通過(guò)繼承來(lái)實(shí)現(xiàn)對(duì)給定類的功能擴(kuò)展。
裝飾模式 VS 繼承
裝飾模式 繼承
用來(lái)擴(kuò)展特定對(duì)象的功能 用來(lái)擴(kuò)展一類對(duì)象的功能
不需要子類 需要子類
動(dòng)態(tài)地 靜態(tài)地
運(yùn)行時(shí)分配職責(zé) 編譯時(shí)分派職責(zé)
防止由于子類而導(dǎo)致的復(fù)雜和混亂 導(dǎo)致很多子類產(chǎn)生,在一些場(chǎng)合,報(bào)漏類的層次
更多的靈活性 缺乏靈活性
例子:
讓我們重新返回我們?cè)诠S方法和單例模式log實(shí)用工具上,我們的模式主要由Logger 接口和兩個(gè)它的實(shí)現(xiàn)類??FileLogger和ConsoleLogger??分別把信息出力到一個(gè)文件和屏幕中。另外,還有包括工廠方法的LoggerFactory類。
LoggerFactory沒(méi)有出現(xiàn)在下圖中,主要是因?yàn)樗同F(xiàn)在討論的例子沒(méi)有直接聯(lián)系。
讓我們想象一些客戶端需要以超出Logger Utility現(xiàn)在所提供的新的方式出力信息,客戶端需要下面兩種特征;
(1) 把出力的信息傳喚為HTML文檔
(2) 對(duì)出力信息進(jìn)行邏輯轉(zhuǎn)化的簡(jiǎn)單加密,在面向?qū)ο蟮脑O(shè)計(jì)中,不改變現(xiàn)存的類的代碼,可以應(yīng)用繼承來(lái)增加新的功能。例如,子類化現(xiàn)在的類重載它的方法來(lái)增加所需要的新功能。
應(yīng)用繼承,我們要子類化FileLogger和ConsoleLogger類來(lái)增加新的功能,會(huì)有下面的一組新的子類:
子類 父類 功能
HTMLFileLogger FileLogger 轉(zhuǎn)化出力信息為HTML文檔,并存入一個(gè)Log文件
HTMLConsLogger ConsoleLogger 轉(zhuǎn)化出力信息為HTML文檔,并顯示在屏幕上
EncFileLogger FileLogger 加密出力信息,并存入一個(gè)Log文件
EncConsLogger ConsoleLogger 加密出力信息,并顯示在屏幕上
從類圖可以看到,為了實(shí)現(xiàn)新的功能加入了一組新的子類。如果我們還有其他的Logger類型(例如:DBLogger出力信息到數(shù)據(jù)庫(kù)中),這樣會(huì)有更多子類。當(dāng)一個(gè)新的特性需要被加入,子類的數(shù)量會(huì)有成倍數(shù)的增長(zhǎng),同時(shí)我們會(huì)有一個(gè)龐大的類層次。
裝飾模式使我們從這種情景中解脫出來(lái),裝飾模式推薦通過(guò)對(duì)象的合成而不是繼承來(lái)包裝一個(gè)對(duì)象擴(kuò)展它的功能。
應(yīng)用裝飾模式,讓我們?yōu)長(zhǎng)ogger Utility定義一個(gè)有下列特征的默認(rèn)根裝飾類LoggerDecorator:
(1) LoggerDecorator包括一個(gè)Logger實(shí)例的引用。這個(gè)引用指向它包含的Logger對(duì)象。
(2) LoggerDecorator實(shí)現(xiàn)Logger借口、提供Log方法的基本的默認(rèn)實(shí)現(xiàn),他只是簡(jiǎn)單的轉(zhuǎn)發(fā)調(diào)用給它包含的Logger 對(duì)象。每一個(gè)LoggerDecorator子類保證定義log方法。
Listing 19.1: LoggerDecorator Class
- public class LoggerDecorator implements Logger {
- Logger logger;
- public LoggerDecorator(Logger inp_logger) {
- logger = inp_logger;
- }
- public void log(String DataLine) {
- /*
- Default implementation
- to be overriden by subclasses.
- */
- logger.log(DataLine);
- }
- }//end of class
每一個(gè)logger的裝飾定義log方法使很重要的,因?yàn)檠b飾對(duì)象必須提供和它包裝的對(duì)象相同的借口。當(dāng)客戶端創(chuàng)建一個(gè)裝飾類的實(shí)例,客戶端以與裝飾類交互方式和客戶端與擁有相同接口原對(duì)象的交互方式是一致的。
讓我們定義LoggerDecorator的兩個(gè)子類,HTMLLogger和EncryptLogger。 #p# 具體的Logger 裝飾類
HTMLLogger
HTMLLogger重載了log方法的默認(rèn)實(shí)現(xiàn)。在log方法中,裝飾類把出力信息轉(zhuǎn)化為HTML文檔,并且發(fā)送給可以出力的Logger實(shí)例。
Listing 19.2: HTMLLogger Class
- public class HTMLLogger extends LoggerDecorator {
- public HTMLLogger(Logger inp_logger) {
- super(inp_logger);
- }
- public void log(String DataLine) {
- /*
- Added functionality
- */
- DataLine = makeHTML(DataLine);
- /*
- Now forward the encrypted text to the FileLogger
- for storage
- */
- logger.log(DataLine);
- }
- public String makeHTML(String DataLine) {
- /*
- Make it into an HTML document.
- */
- DataLine = "" + """ + DataLine +
- " + "";
- return DataLine;
- }
- }//end of class
EncryptLogger
與HTMLLogger相似,EncryptLogger重載了log方法,在log方法中,EncryptLogger通過(guò)簡(jiǎn)單的將字符位置向右轉(zhuǎn)移一位實(shí)現(xiàn)了加密邏輯,并且發(fā)送給可以出力的Logger實(shí)例。
Listing 19.3: EncryptLogger Class
- public class EncryptLogger extends LoggerDecorator {
- public EncryptLogger(Logger inp_logger) {
- super(inp_logger);
- }
- public void log(String DataLine) {
- /*
- Added functionality
- */
- DataLine = encrypt(DataLine);
- /*
- Now forward the encrypted text to the FileLogger
- for storage
- */
- logger.log(DataLine);
- }
- public String encrypt(String DataLine) {
- /*
- Apply simple encryption by Transposition…
- Shift all characters by one position.
- */
- DataLine = DataLine.substring(DataLine.length() − 1) +
- DataLine.substring(0, DataLine.length() − 1);
- return DataLine;
- }
- }//end of class
為了使用新設(shè)計(jì)裝飾對(duì)象,客戶端需要:
(1) 使用LoggerFactory工廠方法創(chuàng)建一個(gè)合適的Logger實(shí)例(FileLogger/ConsoleLogger)。
(2) 把第一步中創(chuàng)建的Logger實(shí)例作為參數(shù)轉(zhuǎn)遞給新創(chuàng)建的合適的LoggerDecorator實(shí)例的構(gòu)造函數(shù)。
(3) 調(diào)用LoggerDecorator實(shí)例上的方法,
Listing 19.4: Client DecoratorClient Class
- class DecoratorClient {
- public static void main(String[] args) {
- LoggerFactory factory = new LoggerFactory();
- Logger logger = factory.getLogger();
- HTMLLogger hLogger = new HTMLLogger(logger);
- //the decorator object provides the same interface.
- hLogger.log("A Message to Log");
- EncryptLogger eLogger = new EncryptLogger(logger);
- eLogger.log("A Message to Log");
- }
- }//End of class
增加新的信息出力類型
在Logging Utility實(shí)例中,應(yīng)用裝飾模式對(duì)比使用繼承不會(huì)因?yàn)轭悓哟蔚脑鲩L(zhǎng)而導(dǎo)致大量的子類,我們還有另外的Logger類型:DBLogger??出力信息到數(shù)據(jù)庫(kù)中。為了將信息轉(zhuǎn)化HTML格式或在出力到數(shù)據(jù)庫(kù)以前對(duì)信息進(jìn)行加密,客戶端只需遵從上面提到的步驟,因?yàn)镈BLogger是一種Logger類型,它可以作為構(gòu)造函數(shù)的參數(shù)傳遞給HTMLLogger或EncryptLogger中任何一個(gè)類。
增加新的裝飾
從例子中可以看到,LoggerDecorator實(shí)例包含了一個(gè)Logger類型了對(duì)象實(shí)例,在轉(zhuǎn)發(fā)請(qǐng)求給Logger對(duì)象實(shí)例以前或以后,增加新的功能。因?yàn)長(zhǎng)oggerDecorator類實(shí)現(xiàn)了Logger接口,LoggerDecorator實(shí)例或它的任何一個(gè)子類都可以作為一個(gè)Logger類型。因此LoggerDecortator包含它的任何子類的一個(gè)實(shí)例,并且將請(qǐng)求轉(zhuǎn)發(fā)給它/。一般的一個(gè)裝飾對(duì)象可以包含另一個(gè)裝飾對(duì)象,并且可以向它轉(zhuǎn)發(fā)請(qǐng)求。通過(guò)這種方式,新的裝飾類,新的功能可以通過(guò)包裝現(xiàn)存的裝飾類來(lái)實(shí)現(xiàn)。
posted on 2009-11-01 10:38 liyang 閱讀(959) 評(píng)論(0) 編輯 收藏 所屬分類: design pattern