連載:麵向對象葵花寶典:思想、技巧與實踐(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