裝飾器模式 Decorator

裝飾器模式 Decorator

動機(Motivation)

  • 在某些狀況下,咱們可能會過分地使用繼承來擴展對象的功能,因爲繼承爲類型引入的靜態特質,使得這種擴展方式缺少靈活性;而且隨着子類的增多(擴展功能的增多),各類子類的組合(擴展功能的組合)會致使更多子類的膨脹。
  • 如何使「對象功能的擴展」可以根據須要來動態地實現?同時避免「擴展功能的增多」帶來的子類膨脹問題?從而使得任何「功能擴展變化」所致使的影響降爲最低?

模式定義

動態(組合)地給一個對象增長一些額外的職責。就增長功能而言,Decorator 模式比生成子類(繼承)更爲靈活(消除重複代碼 & 減小子類個數)。——《設計模式》GoFjava

結構(Structure)

裝飾器模式類圖

顯著特徵

若是在代碼中出現以下相似代碼:git

public class ClassA implements InterfaceA {
    InterfaceA a;
    // ...
}

一個類既實現了 InterfaceA 接口,又包含 InterfaceA 字段,那麼在這段代碼的位置,幾乎能夠確定使用了裝飾器模式。其中,實現 InterfaceA 是爲了確保 ClassA 保留 InterfaceA 接口的規範,而包含 InterfaceA 字段是爲了利用組合實現多態。github

要點總結

  • 經過採用組合而非繼承的手法,Decorator 模式實現了在運行時動態擴展對象功能的能力,並且能夠根據須要擴展多個功能。避免了使用繼承帶來的「靈活性差」和「多子類衍生問題」。
  • Decorator 類在接口上表現爲 is-a Component 的繼承關係,即 Decorator 類繼承了 Component 類所具備的接口。但在實現上又表現爲 has-a Component 的組合關係,即 Decorator 類又使用了另一個 Component 類。
  • Decorator 模式的目的並不是解決「多子類衍生的多繼承」問題,Decorator 模式應用的要點在於解決「主體類在多個方向上的擴展功能」——是爲「裝飾」的含義。

理解與代碼設計

裝飾器,顧名思義,須要裝飾誰?這個「誰」就是裝飾器模式的主體,這個主體的具體實現通常會有規範它行爲特質的接口(接口就是規範),咱們能夠經過實現這個接口添加一個抽象裝飾器(爲了進一步的擴展裝飾器的能力,因此這個裝飾器爲抽象的,或者稱之爲非葉子節點的,非最終使用的裝飾器),當須要爲主體對象擴展某些功能或者附加某些責任時,再建立具體的裝飾器對象去繼承上面的抽象裝飾器,達到擴展主體對象功能或附加責任的目的。綜上,代碼設計思路以下:設計模式

  • 明確須要裝飾(擴展、附加責任)的主體對象如 ClassA ,示例代碼以下:ide

    public class ClassA {
        @Override
        public void eat() {
            System.out.println("吃飯");
        }
    }
  • 抽象出該主體對象的接口(規範)如 InterfaceA ,示例代碼以下:測試

    public interface InterfaceA {
        void eat();
    }
  • 建立抽象裝飾器如 DecoratorA ,示例代碼以下(裝飾器模式顯著代碼特徵):this

    public abstract class DecoratorA implements InterfactA {
        protected InterfaceA interfaceA;
    }
  • 建立具體的裝飾器,用來擴展主體對象的功能,如 ConcreteDecoratorA ,示例代碼以下:spa

    public class ConcreteDecoratorA extends DecoratorA {
        public ConcreteDecoratorA (InterfaceA interfaceA) {
            this.interfaceA = interfaceA;
        }
        
        // 下方重寫 `override` InterfaceA 中的方法,作功能增強
        @Override
        public void eat() {
            // 功能增強
            System.out.println("飯前洗手");
            // 調用主體類方法,執行功能
            this.interfaceA.eat();
            // 功能增強
            System.out.println("飯後洗手");
        }
    }
  • 測試示例代碼以下:設計

    @Test
    public void concreteDecoratorTest() {
        // 不使用裝飾器
        System.out.println("不使用裝飾器:");
        InterfaceA classA = new ClassA();
        classA.eat();
    
        System.out.println();
        System.out.println("-------------------------------------------");
        System.out.println();
    
        // 使用裝飾器
        System.out.println("使用裝飾器:");
        InterfaceA concreteDecoratorA = new ConcreteDecoratorA(new ClassA());
        concreteDecoratorA.eat();
    }

    輸出以下所示:code

    不使用裝飾器:
    吃飯
    
    -------------------------------------------
    
    使用裝飾器:
    飯前洗手
    吃飯
    飯後洗手

代碼實現

模擬小明「吃飯」和「找對象」場景:

小明-裝飾器模式

相關文章
相關標籤/搜索