設計模式之裝飾模式(Decorator)詳解及代碼示例


一、裝飾模式的定義

  裝飾(Decorator)模式的定義:指在不改變現有對象結構的情況下,動態地給該對象增加一些職責(即增加其額外功能)的模式,它屬於對象結構型模式。

二、裝飾模式優缺點

  裝飾(Decorator)模式的主要優點有:

  • 采用裝飾模式擴展對象的功能比采用繼承方式更加靈活。
  • 可以設計出多個不同的具體裝飾類,創造出多個不同行為的組合。

  其主要缺點是:

  裝飾模式增加了許多子類,如果過度使用會使程序變得很復雜。

三、裝飾模式的實現

  通常情況下,擴展一個類的功能會使用繼承方式來實現。但繼承具有靜態特征,耦合度高,並且隨着擴展功能的增多,子類會很膨脹。如果使用組合關系來創建一個包裝對象(即裝飾對象)來包裹真實對象,並在保持真實對象的類結構不變的前提下,為其提供額外的功能,這就是裝飾模式的目標。下面來分析其基本結構和實現方法。

  裝飾模式主要包含以下角色。

  • 抽象構件(Component)角色:定義一個抽象接口以規范准備接收附加責任的對象。
  • 具體構件(Concrete    Component)角色:實現抽象構件,通過裝飾角色為其添加一些職責。
  • 抽象裝飾(Decorator)角色:繼承抽象構件,並包含具體構件的實例,可以通過其子類擴展具體構件的功能。
  • 具體裝飾(ConcreteDecorator)角色:實現抽象裝飾的相關方法,並給具體構件對象添加附加的責任。

  裝飾模式的結構圖如圖所示:

            

  我們先來看看我們通過繼承的方式新增特性這種實現方式,比如本例使用煎餅果子,代碼如下:

/**
 * 煎餅
 */
public class Battercake {
    protected String getDesc(){
        return "煎餅";
    }
    protected int cost(){
        return 8;
    }
}

/**
 * 加蛋的煎餅
 */
public class BattercakeWithEgg extends Battercake {
    @Override
    public String getDesc() {
        return super.getDesc()+" 加一個雞蛋";
    }

    @Override
    public int cost() {
        return super.cost()+1;
    }
}

/**
 * 加蛋加香腸的煎餅
 */
public class BattercakeWithEggSausage extends BattercakeWithEgg {
    @Override
    public String getDesc() {
        return super.getDesc()+ " 加一根香腸";
    }

    @Override
    public int cost() {
        return super.cost()+2;
    }
}

public class Test {
    public static void main(String[] args) {
        Battercake battercake = new Battercake();
        System.out.println(battercake.getDesc()+" 銷售價格:"+battercake.cost());

        Battercake battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getDesc()+" 銷售價格:"+battercakeWithEgg.cost());

        Battercake battercakeWithEggSausage = new BattercakeWithEggSausage();
        System.out.println(battercakeWithEggSausage.getDesc()+" 銷售價格:"+battercakeWithEggSausage.cost());
    }
}

  最后測試結果為:

煎餅 銷售價格:8
煎餅 加一個雞蛋 銷售價格:9
煎餅 加一個雞蛋 加一根香腸 銷售價格:11

  雖然我們也實現了擴展類的功能,但是繼承的方式耦合度高,並且如果新增會無限增加類,如果修改原有類,對后面的類影響很大,因此如果使用裝飾模式,代碼如下:

public class DecoratorPattern
{
    public static void main(String[] args)
    {
        Component p=new ConcreteComponent();
        p.operation();
        System.out.println("---------------------------------");
        Component d=new ConcreteDecorator(p);
        d.operation();
    }
}
//抽象構件角色 interface Component { public void operation(); }
//具體構件角色 class ConcreteComponent implements Component { public ConcreteComponent() { System.out.println("創建具體構件角色"); } public void operation() { System.out.println("調用具體構件角色的方法operation()"); } }
//抽象裝飾角色 class Decorator implements Component { private Component component; public Decorator(Component component) { this.component=component; } public void operation() { component.operation(); } }
//具體裝飾角色 class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); } public void operation() { super.operation(); addedFunction(); } public void addedFunction() { System.out.println("為具體構件角色增加額外的功能addedFunction()"); } }

四、裝飾模式的應用場景

  前面講解了關於裝飾模式的結構與特點,下面介紹其適用的應用場景,裝飾模式通常在以下幾種情況使用。

  • 當需要給一個現有類添加附加職責,而又不能采用生成子類的方法進行擴充時。例如,該類被隱藏或者該類是終極類或者采用繼承方式會產生大量的子類。
  • 當需要通過對現有的一組基本功能進行排列組合而產生非常多的功能時,采用繼承關系很難實現,而采用裝飾模式卻很好實現。
  • 當對象的功能要求可以動態地添加,也可以再動態地撤銷時。

  裝飾模式在 Java 語言中的最著名的應用莫過於 Java I/O 標准庫的設計了。例如,InputStream 的子類 FilterInputStream,OutputStream 的子類 FilterOutputStream,Reader 的子類 BufferedReader 以及 FilterReader,還有 Writer 的子類 BufferedWriter、FilterWriter 以及 PrintWriter 等,它們都是抽象裝飾類。

  下面代碼是為 FileReader 增加緩沖區而采用的裝飾類 BufferedReader 的例子:

BufferedReader in=new BufferedReader(new FileReader("filename.txtn));
String s=in.readLine();

五、裝飾模式擴展

  裝飾模式所包含的 4 個角色不是任何時候都要存在的,在有些應用環境下模式是可以簡化的,如以下兩種情況。

  • 如果只有一個具體構件而沒有抽象構件時,可以讓抽象裝飾繼承具體構件,其結構圖如圖所示:

                

  •  如果只有一個具體裝飾時,可以將抽象裝飾和具體裝飾合並,其結構圖如圖所示:

               


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM