1. 程式人生 > >設計模式-09裝飾模式(Decorator Pattern)

設計模式-09裝飾模式(Decorator Pattern)

1.模式動機

一般有兩種方式可以實現給一個類或物件增加行為:

  • 繼承機制:使用繼承機制是給現有類新增功能的一種有效途徑,通過繼承一個現有類可以使得子類在擁有自身方法的同時還擁有父類的方法。但是這種方法是靜態的,使用者不能控制增加行為的方式和時機。
  • 關聯機制:即將一個類的物件嵌入另一個物件中,由另一個物件來決定是否呼叫嵌入物件的行為以便擴充套件自己的行為,我們稱這個嵌入的物件為裝飾器(Decorator)

裝飾模式以對客戶透明的方式動態地給一個物件附加上更多的責任,換言之,客戶端並不會覺得物件在裝飾前和裝飾後有什麼不同。裝飾模式可以在不需要創造更多子類的情況下,將物件的功能加以擴充套件。這就是裝飾模式的模式動機。

2.模式定義

裝飾模式(Decorator Pattern):動態地給一個物件增加一些額外的職責(Responsibility),就增加物件功能來說,裝飾模式比生成子類實現更為靈活。其別名也可以稱為包裝器(Wrapper),與介面卡模式的別名相同,但它們適用於不同的場合。根據翻譯的不同,裝飾模式也有人稱之為“油漆工模式”。

屬於 物件結構型模式。

3.模式結構

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

  • 抽象構件角色:定義一個抽象介面以規範準備接收附加責任的物件。
  • 具體構件角色:實現抽象構件,通過裝飾角色為其新增一些職責。
  • 抽象裝飾角色:繼承抽象構件,幷包含具體構件的例項,可以通過其子類擴充套件具體構件的功能。
  • 具體裝飾角色:實現抽象裝飾的相關方法,並給具體構件物件新增附加的責任。

4.模式程式碼

# 抽象構建角色
public interface Component {
    void operation();
}

# 具體構建角色
public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}

# 抽象裝飾角色
public abstract class Decorator implements Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}

# 具體裝飾角色
public class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        doSomething();
    }

    private void doSomething() {
        System.out.println("do something");
    }
}

# Client
public class Client {
    public static void main(String[] args) {
        System.out.println("未裝飾前:");
        Component component = new ConcreteComponent();
        component.operation();

        System.out.println("裝飾後:");
        Decorator decorator = new ConcreteDecorator(component);
        decorator.operation();

        // 也可以寫為
        Component decoratorComponent = new ConcreteDecorator(component);
        decoratorComponent.operation();
    }
}

裝飾前也是可以單獨執行的,裝飾器只是增加了新功能而已,將 component 包裝了一下,增加一些新功能,然後對外提供服務。

由於 Decorator 也是 Component 的實現類,所以,可以無縫的把原先的 ConcreteComponent 類替換了,使用 ConcreteDecorator 來提供服務。

5.總結

分析

  • 與繼承關係相比,關聯關係的主要優勢在於不會破壞類的封裝性,而且繼承是一種耦合度較大的靜態關係,無法在程式執行時動態擴充套件。在軟體開發階段,關聯關係雖然不會比繼承關係減少編碼量,但是到了軟體維護階段,由於關聯關係使系統具有較好的鬆耦合性,因此使得系統更加容易維護。當然,關聯關係的缺點是比繼承關係要建立更多的物件。
  • 使用裝飾模式來實現擴充套件比繼承更加靈活,它以對客戶透明的方式動態地給一個物件附加更多的責任。裝飾模式可以在不需要創造更多子類的情況下,將物件的功能加以擴充套件。

優點

  • 裝飾模式與繼承關係的目的都是要擴充套件物件的功能,但是裝飾模式可以提供比繼承更多的靈活性。
  • 可以通過一種動態的方式來擴充套件一個物件的功能,通過配置檔案可以在執行時選擇不同的裝飾器,從而實現不同的行為。
  • 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合。可以使用多個具體裝飾類來裝飾同一物件,得到功能更為強大的物件。
  • 具體構件類與具體裝飾類可以獨立變化,使用者可以根據需要增加新的具體構件類和具體裝飾類,在使用時再對其進行組合,原有程式碼無須改變,符合“開閉原則”

缺點

  • 使用裝飾模式進行系統設計時將產生很多小物件,這些物件的區別在於它們之間相互連線的方式有所不同,而不是它們的類或者屬性值有所不同,同時還將產生很多具體裝飾類。這些裝飾類和小物件的產生將增加系統的複雜度,加大學習與理解的難度。
  • 這種比繼承更加靈活機動的特性,也同時意味著裝飾模式比繼承更加易於出錯,排錯也很困難,對於多次裝飾的物件,除錯時尋找錯誤可能需要逐級排查,較為煩瑣。