設計模式——裝飾模式

  • 2020 年 9 月 21 日
  • 筆記

裝飾模式

裝飾模式能給一個對象動態添加一些額外的職責。就增加功能來說, Decorator 模式相比生成子類更為靈活。

裝飾模式結構

在裝飾模式中各個角色有:

  • 抽象構件(Component)角色: 給出一個抽象介面,以規範準備接收附加責任的對象。
  • 具體構件(Concrete Component)角色:定義一個要接收附加責任的類。
  • 裝飾(Decorator)角色:持有一個構件(斷腸和)對象的實例,並定義一個與抽象構件介面一致的介面。
  • 具體裝飾(Decorator)角色:負責給構件對象 「貼上」附加的責任。

例子:

  1. 裝飾模式動態地為對象附加額外的責任。添加到松樹或冷杉樹上的裝飾品就是裝飾模式的例子。燈光、花環、手杖糖果、玻璃裝飾品等,都可以添加到樹上,讓它看起來更喜慶。這些裝飾物不會改變聖誕樹本身,不管使用什麼特殊的裝飾物,聖誕樹都可以被認作聖誕樹。作為附加功能的一個例子,燈的添加允許人們「點亮」聖誕樹。
  2. 另一個例子:突擊槍本身就是致命武器。但你可以應用某些「裝飾」使它更精確、更安靜、更具破壞性。

我們再從程式碼層面看一個例子:

使用裝飾模式前


package DecoratorPattern.Before;


/**
 * 使用裝飾模式前
 */

class A {
    public void doIt(){
        System.out.print('A');
    }
}

class AwithX extends A {
    @Override
    public void doIt() {
        super.doIt();
        doX();
    }

    private void doX(){
        System.out.print('X');
    }
}

class AwithY extends A {
    @Override
    public void doIt() {
        super.doIt();
        doY();
    }

    public void doY(){
        System.out.print('Y');
    }
}

class AwithZ extends A {
    @Override
    public void doIt() {
        super.doIt();
        doZ();
    }

    public void doZ() {
        System.out.print('Z');
    }
}

class AwithXY extends AwithX {

    private AwithY obj = new AwithY();

    @Override
    public void doIt() {
        super.doIt();
        obj.doY();
    }
}

class AwithXYZ extends AwithX {

    private AwithY obj1 = new AwithY();
    private AwithZ obj2 = new AwithZ();

    @Override
    public void doIt() {
        super.doIt();
        obj1.doY();
        obj2.doZ();
    }
}

public class DecoratorDemo{
    public static void main(String[] args) {
        A[] array = {new AwithX(), new AwithXY(), new AwithXYZ()};
        for (A a: array){
            a.doIt();
            System.out.print(' ');
        }
    }
}

使用裝飾模式後


package DecoratorPattern.After;

/**
 * 使用裝飾模式後
 */

interface I {
    void doIt();
}

class A implements I {

    @Override
    public void doIt() {
        System.out.print('A');
    }
}

abstract class D implements I {
    private I core;

    public D(I inner) {
        core = inner;
    }

    @Override
    public void doIt() {
        core.doIt();
    }
}

class X extends D {

    public X(I inner) {
        super(inner);
    }

    @Override
    public void doIt() {
        super.doIt();
        doX();
    }

    private void doX() {
        System.out.print('X');
    }
}

class Y extends D {

    public Y(I inner) {
        super(inner);
    }

    @Override
    public void doIt() {
        super.doIt();
        doY();
    }

    private void doY() {
        System.out.print('Y');
    }
}

class Z extends D {

    public Z(I inner) {
        super(inner);
    }

    @Override
    public void doIt() {
        super.doIt();
        doZ();
    }

    private void doZ() {
        System.out.print('Z');
    }
}

public class DecoratorDemo {
    public static void main(String[] args) {
      I[] array = {new X(new A()), new Y(new X(new A())), new Z(new Y(new X(new A())))};
      for (I anArray: array){
          anArray.doIt();
          System.out.print(' ');
      }
    }
}


優點:

  1. 裝飾模式比靜態繼續更靈活。與對象的靜態繼承相比,裝飾模式提供了更加靈活地向對象添加職責的方式。可以用添加和分享的方法,用裝飾在運行時和刪除職責。相比之下,繼承機制要求為每個添加的職責創建一個新的子類。這會產生許多新的類,並且會增加系統複雜度。此外,為下個特定的 Component 類提供多個不同的 Decorator 類,這就使得你可以對一些職責進行混合和匹配。
  2. 避免在層次結構高層的類有太多的特徵。裝飾模式提供了一種」即用即付「的方法來添加職責。它並不試圖在一個複雜的可訂製的類中支援所有可預見的特徵,相反,你可以定義一個簡單的類,並且用 Decorator 類給它逐漸地添加功能。可以從簡單的部件組合出複雜的功能。這樣,應用程式不必為不需要的特徵付出代價。

缺點:

  1. Decorator 與它的 Component 不一樣。Decorator 是一個透明的包裝。如果我們從對象標識的觀點出發,一個被裝飾了的組件與這個組件是有差別的,因此,使用裝飾時不應該依賴對象標識。
  2. 有許多小對象。採用裝飾模式進行系統設計往往會產生許多看上去類似的小對象,這些對象僅僅在它們相互連接的方式上有所不同,而不是它們的類或是它們的屬性值有所不同。儘管對天那些了解這些系統的人來說,很容易對它們進行訂製,但是很難學習這些系統,排錯也很困難。

參考:

//sourcemaking.com/design_patterns/decorator