設計模式學習筆記(3)裝飾器

擺碼王子發表於2019-02-03

本文例項程式碼:github.com/JamesZBL/ja…

裝飾器(Decorator)模式用於動態地給一個物件新增一些額外的職責。 就增加功能來說, Decorator模式相比生成子類更為靈活。裝飾模式以對客戶端透明的方式擴充套件物件的功能,是繼承關係的一個替代方案。

純粹的裝飾模式很難找到,大多數的裝飾模式的實現都是“半透明”的,而不是完全透明的。換言之,允許裝飾模式改變介面,增加新的方法。半透明的裝飾模式是介於裝飾模式和介面卡模式之間的。介面卡模式的用意是改變所考慮的類的介面,也可以通過改寫一個或幾個方法,或增加新的方法來增強或改變所考慮的類的功能。 大多數的裝飾模式實際上是半透明的裝飾模式,這樣的裝飾模式也稱做半裝飾、半介面卡模式。

適用場景

以下情況使用Decorator模式

  • 在不影響其他物件的情況下, 以動態、 透明的方式給單個物件新增職責。

  • 處理那些可以撤消的職責。

  • 當不能採用生成子類的方法進行擴充時。 一種情況是, 可能有大量獨立的擴充套件, 為支援每一種組合將產生大量的子類, 使得子類數目呈爆炸性增長。 另一種情況可能是因為類定義被隱藏, 或類定義不能用於生成子類。

模式要點

image

組成部分

  • Component:定義一個物件介面, 可以給這些物件動態地新增職責。

  • ConcreteComponent:定義一個物件, 可以給這個物件新增一些職責。

  • Decorator:持有一個指向 Component 物件的引用,並定義一個與 Component 介面一致的介面。

  • ConcreteDecorator:一向元件新增職責。

協作原理

  • Decorator 將請求轉發給它的 Component 物件, 並有可能在轉發請求前後執行一些附加的動作。

例項分析

[站外圖片上傳中…(image-a0ad13-1526278884853)]

鐵匠和木匠同時製作一把鐵錘,第一種方案是木匠製作錘把,鐵匠製作錘頭;第二中方案是鐵匠先製作錘把再製作錘頭(假定這裡的木匠只會製作錘把)。製作過程分為三部分:1.對材料進行初步的檢查,2.進行製造並把部件安裝起來以供後面的操作,3.完成之後再次進行檢查,確保沒有質量問題。

首先定義“操作”介面,包括前後兩次檢查以及安裝的操作。


/**

 * 流水線上操作行為的介面

 */

public interface Operation {

  void checkBefore();

  void join();

  void chekcAfter();

}

複製程式碼

現在只由木匠製作錘把,定義一個木匠的操作類 CarpenterOperation


/**

 * 木匠的工作

 */

public class CarpenterOperation implements Operation {

  private static final Logger LOGGER = LoggerFactory.getLogger(CarpenterOperation.class);

  @Override

  public void checkBefore() {

    LOGGER.info("檢查木材");

  }

  @Override

  public void join() {

    LOGGER.info("打造錘把");

  }

  @Override

  public void chekcAfter() {

    LOGGER.info("檢查成品錘把");

  }

}

複製程式碼

由於某些原因,鐵匠決定自己製作錘把,現在鐵匠身兼雙職,將木匠的工作也承擔了。定義一個鐵匠操作類 HammerSmith


/**

 * 鐵匠

 */

public class HammerSmithOperation implements Operation {

  private static final Logger LOGGER = LoggerFactory.getLogger(HammerSmithOperation.class);

  private Operation previousOperation;

  public HammerSmithOperation(Operation previousOperation) {

    this.previousOperation = previousOperation;

  }

  @Override

  public void checkBefore() {

    previousOperation.checkBefore();

    LOGGER.info("檢查鐵材");

  }

  @Override

  public void join() {

    previousOperation.join();

    LOGGER.info("打造錘頭");

  }

  @Override

  public void chekcAfter() {

    previousOperation.chekcAfter();

    LOGGER.info("檢查成品錘頭");

  }

}

複製程式碼

同樣實現了“操作”的介面,鐵匠的每個操作都包含了木匠相應的操作,相當於對木匠的操作增加了一層包裹和擴充套件。這種包裝就是 Decorator 模式中的裝飾。

現在分別讓木匠和鐵匠進行一系列操作


/**

 * Decorator

 */

public class Application {

  private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

  public static void main(String[] args) {

    LOGGER.info("僅由木匠製作錘把");

    Operation carpenter = new CarpenterOperation();

    carpenter.checkBefore();

    carpenter.join();

    carpenter.chekcAfter();

    LOGGER.info("由鐵匠完成錘把以及錘頭的製作");

    Operation hammerSmith = new HammerSmithOperation(carpenter);

    hammerSmith.checkBefore();

    hammerSmith.join();

    hammerSmith.chekcAfter();

  }

}

複製程式碼

輸出如下內容


    僅由木匠製作錘把

    檢查木材

    打造錘把

    檢查成品錘把

    由鐵匠完成錘把以及錘頭的製作

    檢查木材

    檢查鐵材

    打造錘把

    打造錘頭

    檢查成品錘把

    檢查成品錘頭

複製程式碼

效果

優點

1. 裝飾模式和靜態繼承的機制的作用都是對現有的類增加新的功能,但裝飾模式有著比靜態繼承更靈活的組合方式。裝飾模式可以在執行的時候決定需要增加還是去除一種“裝飾”以及什麼“裝飾”。靜態繼承則沒有這樣的靈活性,它對類功能的擴充套件是在執行之前就確定了的。

2. 得益於裝飾模式在組合上的靈活性和便利性,我們可以將各種裝飾類進行組合,從而較為簡單的創造各種不同的行為集合,實現多種多樣的功能。

缺點

1. 裝飾者的物件和它裝飾的物件本質上是完全不同的,裝飾模式會生成許多的物件,導致區分各種物件變得困難

2. 由於使用相同的標識,對於程式的理解和排錯過程的難度也會隨之增加

個人部落格同步更新,獲取更多技術分享請關注:鄭保樂的部落格

相關文章