設計模式——裝飾模式

二十億光年的孤獨發表於2020-09-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. 有許多小物件。採用裝飾模式進行系統設計往往會產生許多看上去類似的小物件,這些物件僅僅在它們相互連線的方式上有所不同,而不是它們的類或是它們的屬性值有所不同。儘管對天那些瞭解這些系統的人來說,很容易對它們進行定製,但是很難學習這些系統,排錯也很困難。

參考:

https://sourcemaking.com/design_patterns/decorator

相關文章