­

設計模式之模板方法模式(二)

  • 2019 年 12 月 26 日
  • 筆記

上一篇我們已經學會了模板方法模式,這次我們繼續來深入學習下。畢竟學會只是第一步,還有更進一步的學習和深入等著我們呢。

我們先來看下,對模板方法模式的一個總結的類圖:

讓我們細看抽象類是如何被定義的,包含了它內含的模板方法和原語操作。

abstract class AbstractClass {  // 這就是模板方法。它被聲明為final,以免子類改變這個演算法的順序      final void templateMethod() {      // 模板方法定義了一連串的步驟,每個步驟由一個方法代表          primitiveOperation1();          primitiveOperation2();          concreteOperation();          hook();      }        abstract void primitiveOperation1();        abstract void primitiveOperation2();        final void concreteOperation() {          // 這裡是實現      }        // 這是一個具體的方法,但他什麼都不做。我們叫它為hook(鉤子),馬上就來揭曉它如何使用      void hook();  }  

對模板方法進行掛鉤

鉤子是一種被聲明在抽象類中的方法,但只有空的或者默認的實現。鉤子的存在,可以讓子類有能力對演算法的不同點進行掛鉤。要不要掛鉤,由子類決定。

鉤子有很多用途,我們先看其中一個:

public abstract class CaffeineBeverageWithHook {        final void prepareRecipe() {          boilWater();          brew();          pourInCup();          // 我們加上一個小小的條件語句,該條件是否成立,是由一個具體方法決定          if (customerWantsCondiments()) {              addCondiments();          }      }        abstract void brew();        abstract void addCondiments();        void boilWater() {          System.out.println("Boiling water");      }        void pourInCup() {          System.out.println("Pouring into cup");      }        // 這就是一個鉤子,子類可以覆蓋這個方法,但不一定需要使用      boolean customerWantsCondiments() {          return true;      }  }  

為了使用鉤子,我們在子類中覆蓋它。在這裡,鉤子控制了咖啡因飲料是否執行某部分演算法;比如,飲料中是否需要加進調料

public class CoffeeWithHook extends CaffeineBeverageWithHook {        public void brew() {          System.out.println("Dripping Coffee through filter");      }        public void addCondiments() {          System.out.println("Adding Sugar and Milk");      }        public boolean customerWantsCondiments() {          // 詢問用戶,是否要加調料          String answer = getUserInput();            if (answer.toLowerCase().startsWith("y")) {              return true;          } else {              return false;          }      }        private String getUserInput() {          String answer = null;            System.out.print("Would you like milk and sugar with your coffee (y/n)? ");            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));          try {              answer = in.readLine();          } catch (IOException ioe) {              System.err.println("IO error trying to read your answer");          }          if (answer == null) {              return "no";          }          return answer;      }  }  

上面就是一個鉤子,詢問顧客是否想要調料?如果需要,就執行內容,否則就不執行。測試程式碼如下:

public class BeverageTestDrive {      public static void main(String[] args) {            Tea tea = new Tea();          Coffee coffee = new Coffee();            System.out.println("nMaking tea...");          tea.prepareRecipe();            System.out.println("nMaking coffee...");          coffee.prepareRecipe();              TeaWithHook teaHook = new TeaWithHook();          CoffeeWithHook coffeeHook = new CoffeeWithHook();            System.out.println("nMaking tea...");          teaHook.prepareRecipe();            System.out.println("nMaking coffee...");          coffeeHook.prepareRecipe();      }  }  

執行結果如下:茶要加調料就給你加上了;咖啡不需要加調料,就沒給你加

Making tea...  Boiling water  Steeping the tea  Pouring into cup  Would you like lemon with your tea (y/n)? y  Adding Lemon    Making coffee...  Boiling water  Dripping Coffee through filter  Pouring into cup  Would you like milk and sugar with your coffee (y/n)? n  

那麼,我們使用鉤子的真正目的是什麼呢?

鉤子有幾種用法。如我們之前所說的,鉤子可以讓子類實現演算法中可選的部分,或者在鉤子對於子類的實現並不重要的時候,子類可以對此鉤子置之不理。鉤子的另一個用法,是讓子類能夠 有機會對模板方法中某些即將發生的(或剛剛發生的)步驟做出反應。比方說,名為justReOrderedList()的鉤子方法允許子類在內部列表重新組織後執行某些動作(例如在螢幕上重新顯示數據)。正如你剛剛看到的,鉤子也可以讓子類有能力為其抽象類做一些決定。

好萊塢原則

好萊塢原則:別調用(打電話給)我們,我們會調用(打電話給)你。

好萊塢原則可以給我們一種防止「依賴腐敗」的方法。當高層組件依賴低層組件,而低層組件又依賴高層組件,而高層組件又依賴邊側組件,而邊側組件又依賴低層組件時,依賴腐敗就發生了。在這種情況下,沒有人可以輕易地搞懂系統是如何設計的。

在好萊塢原則下,我們允許低層組件將自己掛鉤到系統上,但是高層組件會決定什麼時候和怎樣使用這些低層組件。換句話說,高層組件對待低層組件的方式是「別調用我們,我們會調用你」。

好萊塢原則和模板方法之間的連接其實還算明顯:當我們設計模板方法時,我們告訴子類「不要調用我們,我們會調用你」。怎樣才能辦到呢?讓我們再看一次咖啡因飲料的設計:

我們之前還知道一個原則叫依賴倒置原則,好萊塢原則也是有點這個味道的對吧。他們之間的關係是如何的呢?

依賴倒置原則教我們盡量避免使用具體類,而多實用抽象。而好萊塢原則是用在創建框架或組件上的一種技巧,好讓低層組件能夠被掛鉤進計算中,而且又不會讓高層組件依賴低層組件。兩者的目標都是在於解耦,但是以來倒置原則更加註重如何在設計中避免依賴。

好萊塢原則教我們一個技巧,創建一個有彈性的設計,允許低層結構能夠互相操作,而又防止其他類太過於依賴它們。

這樣我們就把開篇說的隱藏的原則給介紹完了,也更進一步的知道了模板方法模式鉤子的用法,讓我們在實戰中能有一個更好的選擇。這個設計模式,你get到了嗎?

小編本來想在這完結的,但是看了下書,發現後面還有一個更貼近實際的,意想不到的模板方法,而且是我們平時會使用到的,我們下篇來聊聊。

愛生活,愛學習,愛感悟,愛挨踢