設計模式之模板方法模式(帶思考)

源起

  不少設計模式比較簡單,咱們在設計的時候或許都用過,只是不知道它們的名字而已。模板方法模式就是其中一種,相似的還有享元模式。或許聽這名字你會以爲不知所云,但等真正理解其原理的時候你確定會排着大腿說 :OMG,原來這就是模板方法模式。那讀者不由要問了:爲何這麼多設計模式你不寫,只寫模板方法模式,是否是由於它簡單呀?對呀,柿子要挑軟的捏。開個玩笑,最主要緣由最近在工做中用到了這種設計模式,並且即便就這麼簡單的設計模式仍是幾經周折才用上,仍是經驗不夠豐富。話很少說,放碼過來。本文主要分爲兩個部分:java

  1. 模板方法模式相關內容的介紹。
  2. 擴展(一些碎言碎語,可跳過)。

什麼是模板方法模式

腦筋急轉彎

  小明將大象裝進冰箱須要幾個步驟,知道這個腦筋急轉彎的人確定能夠快速的答出有以下三個步驟:算法

  1. 小明把冰箱門打開
  2. 小明把大象放進去
  3. 小明把冰箱門關上

  用 java 實現以下設計模式

class XiaoMingStoreElephantToFridge {
    public void xiaoMingOpenFridgeDoor() {
        System.out.println("小明打開冰箱門");
    }
    
    public void xiaoMingPutIntoFridge() {
       System.out.println("小明將大象裝進冰箱");
    }
    
    public void xiaoMingCloseFridgeDoor() {
       System.out.println("小明關上冰箱門");
    }
    
    //更多時候咱們用一個方法將以上步驟封裝起來,在主方法中(client中)一次調用該方法便可。
    public void xiaoMingStoreElementToFridge(){
        xiaoMingOpenFridgeDoor();
        xiaoMingPutIntoFridge();
        xiaoMingCloseFridgeDoor();
    }
}
複製代碼

  那小紅把螞蟻裝進冰箱須要幾個步驟呢,也是三步(不用把大象拿出來):post

  1. 小紅把冰箱門打開
  2. 小紅把螞蟻放進去
  3. 小紅把冰箱門關上

  用 java 實現以下學習

class XiaoHongStoreAntToFridge {
    public void xiaoHongOpenFridgeDoor() {
        System.out.println("小紅打開冰箱門");
    }
    
    public void xiaoHongPutIntoFridge() {
       System.out.println("小紅將螞蟻裝進冰箱");
    }
    
    public void xiaoHongCloseFridgeDoor() {
       System.out.println("小紅關上冰箱門");
    }
    
    //更多時候咱們用一個方法將以上步驟封裝起來,在主方法中(client中)一次調用該方法便可。
    public void xiaoHongStoreElementToFridge(){
        xiaoHongOpenFridgeDoor();
        xiaoHongPutInfoFridge();
        xiaoHongCloseFridgeDoor();
    }
}
複製代碼

  那把 xxx 放入冰箱須要幾步,這下你能夠堅決果斷的說出須要三步,Balabala。稍微有點設計思想的都知道咱們能夠將小明/小紅將大象/螞蟻裝進冰箱作進一步抽象,以下:網站

  1. 將冰箱門打開。
  2. 將xxx放進冰箱。
  3. 將冰箱門關閉。

  同理,java 類以下:this

class AbstractStoreSomethingToFridge {
    protected abstract void penFridgeDoor();
    
    protected abstract void putIntoFridge();
    
    protected abstract void closeFridgeDoor();
    }
    
    //更多時候咱們用一個方法將以上步驟封裝起來,在主方法中(client中)一次調用該方法便可。
    public final void store() {
        penFridgeDoor();
        putIntoFridge();
        closeFridgeDoor();
    }
}
複製代碼

  該抽象類只關心將物體放入冰箱的有哪些步驟,至因而誰/將什麼放進冰箱該抽象類並不關心,這是子類該作的事。爲了避免佔用過多無用代碼,子類這裏再也不贅述。spa

模板方法模式的定義

  以上 AbstractStoreSomethingToFridge 就是一個模板類,其中 store() 方法就是模板方法。模板方法定義了一個方法的步驟,並容許子類爲一個或者多個步驟提供實現。模板方法通常用 final 修飾,表示不容許在子類中重寫該方法。若是模板方法能夠被重寫,那"模板"將毫無心義,或者說若是要重寫模板方法那說明你要重定義一個模板類了。模板方法模式的 UML 圖以下:設計

  其中:code

  1. AbstractClass 就是模板方法的抽象類,定義了一個模板方法,該方法規定了執行某一流程的規定步驟。
  2. PrimitiveOperation* 是子類必需要實現的步驟。

優缺點與應用場景

優勢

1. 良好的封裝性。把公有的不變的方法封裝在父類,而子類負責實現具體邏輯。
2. 良好的擴展性:增長功能由子類實現基本方法擴展,符合單一職責原則和開閉原則。
3. 複用代碼。
複製代碼

缺點

1. 因爲是經過繼承實現代碼複用來改變算法,靈活度會下降。
2. 子類的執行影響父類的結果,增長代碼閱讀難度。
複製代碼

應用場景

1. 一次性實現一個算法的不變的部分,並將可變的行爲留給子類來實現;
2. 各子類中公共的行爲應被提取出來並集中到一個公共父類中以免代碼重複;
3. 控制子類的擴展。   
複製代碼

總結與思考

模式總結

  模板方法模式是很是常見的模式,常見到咱們經常忽略它的存在。模板方法模式自己有許多種實現,好比在模板方法執行的先後加入鉤子:preActionHook() 和 postActionHook() ,方便子類在模板方法執行以前或者執行後作一些動做。

  模板模式自己比較簡單,難的是如何將要作的事情抽象出固定的、與業務無關的步驟。並非全部的業務都像將大象放入冰箱如此簡單,若是稍微複雜一點的業務不少時候咱們很難跳出細節看到全貌。固然,學會抽象不是一朝一夕的事情,更多的是要學習、模仿和沉澱。

思考感悟

  看懂設計模式 UML 圖不是目的,學會將設計模式應用於實際纔是。我以爲學習設計模式就像學習功夫,有兩種境界:

  第一種是當咱們不知道任何設計模式的時候,咱們冥冥之中也會用。這時候咱們就須要學習、刻意的模仿,將本身知道/不知道的設計方法都往設計模式上套。這一種是記住設計模式。

  第二種是當咱們知道全部設計模式的時候,咱們再也不刻意的將設計方法套到各類設計模式上,也能寫出高內聚低耦合的代碼,作到手中無劍心中有劍。這一種是忘記設計模式。

  目前我還處在第一種境界中苦苦掙扎,但願各位和我在學習設計模式乃至其餘知識都能達到第二種境界。

擴展(可跳過)

實戰經驗

  最近在負責內部門戶網站消息的推送系統,須要作各類推送:日推/周推/自定義推/事件推等,通過幾回思考和重構將推送流程簡化爲如下幾個步驟:

  1. 收集須要推送的內容,放入推送模型。
  2. 將推送模型與 velocity 模板集合。
  3. 獲取接收推送的人信息。
  4. 調用底層通道發送消息推送(站內信/郵件/釘釘等)。

  這種場景下能夠定義一個模板方法模型的抽象類:

class AbstractPush {
    protected void collectModelData();
    
    protected void generateViewContent();
    
    protected void collectReceivers();
    
    //無需子類實現
    private void sendByChannel() {
    }
    
    public final void push() {
        //通用固定模板代碼
        collectModelData();
        generateViewContent();
        collectReceivers();
        sendByChannel();
    }
}
複製代碼

  接着定義子類,並在在 client 中調用子類 push 方法便可。只要知道將推送過程簡化爲以上幾個步驟,套用模板方法模式便水到渠成了。

有限狀態機模式

  模板方法模式中的模板方法每每步驟是固定的且模板方法也是被 final 修飾的。結合有限狀態機的定義,若是:

  1. 模板類中定義起始狀態和結束狀態,其餘狀態由子類來控制。
  2. 模板方法中的每一個步驟(除了開始和結束)的扭轉由子類的狀態來控制。
  3. 模板方法中的步驟數量在子類中能夠控制。

  那模板方法模式就轉變成了有限狀態機模式,僞代碼以下:

public class AbstractStateachine() {
    
    protected Integer state;
    protected void onStart();
    protected abstract void onAction();
    protected void onEnd();
    
    protected final onRun() {
        switch(state) {
            case START:
                onStart();
                this.state = ACTION;
                break;
            case END:
                onEnd();
                break;
            default:
                onAction();
                break;
        }
    }
}
複製代碼

  這樣只要在子類中定義更多的狀態,同時重寫 onAction() 方法便可,可能以下:

public class MyStateMachine() {
    //...其餘省略
    
    protectd void onAction() {
        case CUSTOM_STATE_1:
            onCustomState1Action();
            this.state = CUSTOM_STATE_2;
            break;
        case CUSTOM_STATE_2:
            onCustomState1Action();
            this.state = END;
            break;
          default:
            this.state = END;
            break;
    }
    
    private onCustomState1Action() {
        //balabala1
    }
    
    private onCustomState2Action(){
        //balabala2
    }
}
複製代碼

  固然,以上只是個人一個簡略的想法,有錯誤的地方還請指正。後面還會對有限狀態機模式作一個系統的學習和整理。

引用

  1. 個人Java設計模式-模板方法模式
  2. 模板方法模式(Template Method) - 最易懂的設計模式解析
相關文章
相關標籤/搜索