設計模式-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.總結

分析

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

優點

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

缺點

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