上一篇咱們已經學會了模板方法模式,此次咱們繼續來深刻學習下。畢竟學會只是第一步,還有更進一步的學習和深刻等着咱們呢。java
咱們先來看下,對模板方法模式的一個總結的類圖:算法
讓咱們細看抽象類是如何被定義的,包含了它內含的模板方法和原語操做。設計模式
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(); } }
執行結果以下:茶要加調料就給你加上了;咖啡不須要加調料,就沒給你加code
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
那麼,咱們使用鉤子的真正目的是什麼呢?blog
鉤子有幾種用法。如咱們以前所說的,鉤子可讓子類實現算法中可選的部分,或者在鉤子對於子類的實現並不重要的時候,子類能夠對此鉤子置之不理。鉤子的另外一個用法,是讓子類可以 有機會對模板方法中某些即將發生的(或剛剛發生的)步驟作出反應。比方說,名爲justReOrderedList()的鉤子方法容許子類在內部列表從新組織後執行某些動做(例如在屏幕上從新顯示數據)。正如你剛剛看到的,鉤子也可讓子類有能力爲其抽象類作一些決定。ip
好萊塢原則:別調用(打電話給)咱們,咱們會調用(打電話給)你。
好萊塢原則能夠給咱們一種防止「依賴腐敗」的方法。當高層組件依賴低層組件,而低層組件又依賴高層組件,而高層組件又依賴邊側組件,而邊側組件又依賴低層組件時,依賴腐敗就發生了。在這種狀況下,沒有人能夠輕易地搞懂系統是如何設計的。
在好萊塢原則下,咱們容許低層組件將本身掛鉤到系統上,可是高層組件會決定何時和怎樣使用這些低層組件。換句話說,高層組件對待低層組件的方式是「別調用咱們,咱們會調用你」。
好萊塢原則和模板方法之間的鏈接其實還算明顯:當咱們設計模板方法時,咱們告訴子類「不要調用咱們,咱們會調用你」。怎樣才能辦到呢?讓咱們再看一次咖啡因飲料的設計:
咱們以前還知道一個原則叫依賴倒置原則,好萊塢原則也是有點這個味道的對吧。他們之間的關係是如何的呢?
依賴倒置原則教咱們儘可能避免使用具體類,而多實用抽象。而好萊塢原則是用在建立框架或組件上的一種技巧,好讓低層組件可以被掛鉤進計算中,並且又不會讓高層組件依賴低層組件。二者的目標都是在於解耦,可是以來倒置原則更加註重如何在設計中避免依賴。
好萊塢原則教咱們一個技巧,建立一個有彈性的設計,容許低層結構可以互相操做,而又防止其餘類太過於依賴它們。
這樣咱們就把開篇說的隱藏的原則給介紹完了,也更進一步的知道了模板方法模式鉤子的用法,讓咱們在實戰中能有一個更好的選擇。這個設計模式,你get到了嗎?
小編原本想在這完結的,可是看了下書,發現後面還有一個更貼近實際的,意想不到的模板方法,並且是咱們平時會使用到的,咱們下篇來聊聊。
愛生活,愛學習,愛感悟,愛挨踢