閱讀30 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

  上一篇:go 在Google Drive上建立免費靜態網站
  下一篇:go OCS杭州地域原VIP下線公告