連載:麵向對象葵花寶典:思想、技巧與實踐(40) - DECORATOR模式
掌握了設計模式之道後,我們將以全新的方法來理解設計模式,這個方法更簡單、更直觀,不信?看幾個樣例就知道了
=====================================================================
DECORATOR模式(以設計模式之道來理解)
【業務】
假設你進入了一個信息安全管理非常嚴格的公司,這家公司不允許員工自行打印文檔,所有的文檔打印都需要交給文檔打印係統統一管理。文檔打印係統會記錄每次打印的時間、內容、打印人員。。。。。。等等,以便後續出現問題的時候進行追查。
由於公司有很多的部門,每個部門的安全要求並不完全一樣,同時每個部門關於文檔打印也有自己的一些規定。
我們的任務就是要開發一套能夠支持整個公司文檔打印需求的係統。
【發現變化】
文檔打印係統麵對的變化主要體現在:文檔打印要求是變化的,不同的部門有不同的要求,同一個部門也可能修改自己的打印需求。
例如:
A部門是一個戰略規劃的部門,裏麵的資料都非常重要,打印的時候需要在頁眉位置打印“絕密”,在頁腳的位置打印“密級申明”,同時要加上“絕密文檔”的水印;
B部門是內部培訓部門,打印培訓材料的時候需要在頁眉位置打印“內部公開”,但不需要密級申明,同時加上“培訓資料”的水印
C部門是對外宣傳部門,打印宣傳材料的時候隻需要加上“公司logo”的水印;
【傳統方法】
傳統方法使用類繼承來封裝打印請求,為每個部門創建一個打印的子類。詳細示例代碼如下:
PrintTask.java -- 文檔打印係統開發小組負責維護
package com.oo.designpattern.decorator; /** * 打印任務類 * */ abstract public class PrintTask { abstract public void print(String text); }
SecretPrint.java -- 文檔打印係統開發小組負責維護:
package com.oo.designpattern.decorator; /** * 絕密文檔的打印 * */ public class SecretPrint extends PrintTask{ @Override public void print(String text) { Printer.printHeader("絕密"); Printer.printText(text); Printer.printFooter("本文包含絕密信息,請勿泄露!"); Printer.printTextWaterMark("絕密文檔"); } }
InternalPrint.java -- 文檔打印係統開發小組負責維護:
package com.oo.designpattern.decorator; /** * 內部公開的文檔打印 * */ public class InternalPrint extends PrintTask { @Override public void print(String text) { Printer.printHeader("內部公開"); Printer.printText(text); Printer.printTextWaterMark("培訓資料"); } }
PublicPrint.java -- 文檔打印係統開發小組負責維護:
package com.oo.designpattern.decorator; import java.awt.Image; /** * 對外宣傳的文檔打印 * */ public class PublicPrint extends PrintTask { private Image _logo; @Override public void print(String text) { Printer.printText(text); Printer.printImgWaterMark(_logo); } }
文檔打印係統實現如下:
PrintServer -- 文檔打印係統開發小組負責維護
package com.oo.designpattern.decorator; /** * 文檔打印係統 * */ public class PrintServer { /** * 執行打印任務 * @param task * @param text */ public static void executePrintTask(PrintTask task, String text){ log(); task.print(text); audit(); } /** * 記錄日誌 */ private static void log(){ //省略具體實現代碼 } /** * 記錄審計相關信息 */ private static void audit(){ //省略具體實現代碼 } }
定義好不同的打印任務後,每個部門根據自己的需要,選擇不同的任務發給文檔打印係統。
例如,A部門的打印處理如下:
SecretDepartment.java -- A部門負責維護
package com.oo.designpattern.decorator; /** * A部門的打印處理 * */ public class SecretDepartment { public void print(String text){ PrintTask task = new SecretPrint(); //PrintServer即“文檔打印係統” PrintServer.executePrintTask(task, text); } }
傳統方法使用類繼承來封裝變化的打印需求,當麵對變化時,存在如下問題:
1)新增部門的時候,需要文檔打印係統提供一個新的打印任務類,將導致出現大量的***Print類;
例如:新建了一個D部門,D部門隻需要打印純文本即可,那麼已有的SecretPrint、InternalPrint、PublicPrint類都無法滿足需求,必須新增一個PurePrint的類;
2)某個部門的打印需求變更的時候,需要改變已有的***Print類;
例如:C部門希望在對外宣傳材料的頁眉上打印公司名稱,則需要修改PublicPrint類。
【設計模式方法】
設計模式封裝變化的方法就是Decorator模式。Decorator模式定義如下:
“動態的給一個對象添加一些額外的職責”
《設計模式》一書中關於Decorator模式的描述並不很直觀,我理解Decorator模式為“通過聚合的方式將動態變化的職責組合起來”。
我們詳細看看Decorator模式是如何封裝變化的。
首先,將變化的職責封裝為獨立的類。傳統方式實現中,不同的職責是對應不同的函數調用,而設計模式中,不同的職責是不同的類;
其次,通過聚合將變化的職責組合起來。傳統方式中,不同職責的組合是通過在一個函數中寫多行代碼來體現的,而設計模式中,通過對象的聚合將不同職責組合起來。
【Decorator模式結構】

Component:定義一個對象接口(對應結構圖中的operation函數),可以給這些對象動態添加職責
ConcreteComponent:定義一個對象,這個對象是實際的Component,將被Decorator修飾
Decorator:定義修飾對象的接口,Decorator實現的關鍵在於聚合了一個Component對象
ConcreteDecorator:具體的修飾對象
【代碼實現】
使用Decorator設計模式實現的文檔打印係統代碼如下:
*********************類設計*****************************
PrintComponent.java -- 文檔打印係統開發小組負責維護
package com.oo.designpattern.decorator2; /** * 打印組件的父類 * */ abstract public class PrintComponent { abstract public void print(); }
PrintDecorator.java -- 文檔打印係統開發小組負責維護
package com.oo.designpattern.decorator2; /** * 修飾的打印任務,對應Decorator模式中的Decorator * Decorator可以聚合ConcreteComponent或者其他Decorator * 這樣可以使得打印任務能夠嵌套執行下去,直到最後完成所有打印任務 * */ public abstract class PrintDecorator extends PrintComponent { abstract public void print(); }
TextComponent.java -- 文檔打印係統開發小組負責維護
package com.oo.designpattern.decorator2; import com.oo.designpattern.decorator.Printer; /** * 文本打印,對應Decorator模式中的ConcreteComponent * 打印任務到ConcreteComponent就算真正完成了 * */ public class TextComponent extends PrintComponent { private String _text; TextComponent(String text){ this._text = text; } @Override public void print() { Printer.printText(this._text); } }
HeaderDecorator.java -- 文檔打印係統開發小組負責維護
package com.oo.designpattern.decorator2; import com.oo.designpattern.decorator.Printer; /** * 頁眉打印 * */ public class HeaderDecorator extends PrintDecorator { private PrintComponent _comp; //被修飾的打印組件 private String _text; //需要打印的頁眉內容 /** * 初始化的時候,必須包含其它打印組件comp,這是實現Decorator模式的前提 * 同時也需要指定當前組件所需的參數,不能在print函數的參數中指定, * 因為每個Decorator所需的參數是不一樣的 * @param comp * @param text */ HeaderDecorator(PrintComponent comp, String text) { this._comp = comp; this._text = text; } /** * 打印 */ @Override public void print() { //打印的時候將當前Decorator和被修飾的Component分開,這是Decorator模式的關鍵 Printer.printHeader(_text); //打印頁眉 //_comp本身如果是Decorator,就會嵌套打印下去 //_comp本身如果不是Decorator,而是ConcreteComponent,則打印任務到此結束 _comp.print(); } }
FooterDecorator.java
package com.oo.designpattern.decorator2; import com.oo.designpattern.decorator.Printer; /** * 頁腳打印,和頁眉打印類似,此處省略相同的注釋代碼 * */ public class FooterDecorator extends PrintDecorator { private PrintComponent _comp; private String _text; FooterDecorator(PrintComponent comp, String text) { this._comp = comp; this._text = text; } /** * 打印 */ @Override public void print() { Printer.printFooter(_text); //打印頁腳 _comp.print(); } }
TextWatermarkDecorator.java
package com.oo.designpattern.decorator2; import com.oo.designpattern.decorator.Printer; /** * 文本水印打印,和頁眉打印類似,此處省略相同的注釋代碼 * */ public class TextWatermarkDecorator extends PrintDecorator{ private PrintComponent _comp; private String _text; TextWatermarkDecorator(PrintComponent comp, String text) { this._comp = comp; this._text = text; } /** * 打印 */ @Override public void print() { Printer.printTextWaterMark(_text); //打印文本水印 _comp.print(); } }
ImgWatermarkDecorator.java
package com.oo.designpattern.decorator2; import java.awt.Image; import com.oo.designpattern.decorator.Printer; /** * 圖片水印打印,和頁眉打印類似,此處省略相同的注釋代碼 * */ public class ImgWatermarkDecorator extends PrintDecorator { private PrintComponent _comp; private static Image _logo; //圖片水印隻能是公司logo ImgWatermarkDecorator(PrintComponent comp) { this._comp = comp; } /** * 打印 */ @Override public void print() { Printer.printImgWaterMark(ImgWatermarkDecorator._logo); //打印圖片水印 _comp.print(); } }
PrintServer.java
package com.oo.designpattern.decorator2; public class PrintServer { /** * 執行打印任務 * @param comp */ public static void executePrintTask(PrintComponent comp){ log(); comp.print(); audit(); } /** * 記錄日誌 */ private static void log(){ //省略具體實現代碼 } /** * 記錄審計相關信息 */ private static void audit(){ //省略具體實現代碼 } }
*********************類使用*****************************
A部門的打印處理如下(如下代碼請仔細閱讀,特別是注釋部分):
SecretDepartment.java -- A部門負責維護
package com.oo.designpattern.decorator2; /** * A部門的打印處理,注意與傳統方法中的SecretDepartment類對比 * */ public class SecretDepartment { /** * 打印任務1,對應傳統方式的SecretePrint類 * @param text */ public void print(String text){ /** * 使用Decorator設計模式後,打印任務不再是一個單獨的類SecretPrint類, * 而是通過將多個打印項目聚合成一個打印任務 */ PrintComponent textComp = new TextComponent(text); //注意header聚合了textComp PrintDecorator header = new HeaderDecorator(textComp, "絕密"); //注意footer聚合了header,而不是textComp,這樣就能夠嵌套執行下去 PrintDecorator footer = new FooterDecorator(header, "本文包含絕密信息,請勿泄露!"); //注意watermark聚合了footer,而不是textComp,這樣就能夠嵌套執行下去 PrintDecorator watermark = new TextWatermarkDecorator(footer, "絕密文檔"); //PrintServer即“文檔打印係統”,與傳統的PrintServer相比,這裏不需要知道打印的text內容 //text內容已經封裝到TextComponent中去了(對應代碼行14行) PrintServer.executePrintTask(watermark); //注意這裏傳遞給打印係統的是最後一個Decorator對象watermark } /** * A部門的第二個打印任務,將文本水印改為圖片水印,並且不再打印頁腳 * @param text */ public void print2(String text){ /** * 新增打印任務,無需文檔管理係統增加新的類,隻要A部門自己修改代碼即可 */ PrintComponent textComp = new TextComponent(text); //注意header聚合了textComp PrintDecorator header = new HeaderDecorator(textComp, "絕密"); //注意watermark聚合了header,而不是textComp,這樣就能夠嵌套執行下去 PrintDecorator watermark = new ImgWatermarkDecorator(header); PrintServer.executePrintTask(watermark); } }
可以看到,使用了設計模式的方法後,打印業務的變化,可以通過類似數學上的排列組合已有的打印功能來完成,而不再需要新的打印類了。
================================================
轉載請注明出處:https://blog.csdn.net/yunhua_lee/article/details/38865605
================================================
最後更新:2017-04-03 05:40:04