1、概述
模板設計模式定義:定義一個操作中的算法骨架,將步驟延遲到子類中。
模板設計模式是一種行為設計模式,一般是准備一個抽象類,將部分邏輯以具體方法或者具體的構造函數實現,然后聲明一些抽象方法,這樣可以強制子類實現剩余的邏輯。不同的子類以不同的方式實現這些抽象方法,從而對剩余的邏輯有不同的實現。這就是模板設計模式能達成的功能。
適用於一些復雜操作進行步驟分割、抽取公共部分由抽象父類實現、將不同的部分在父類中定義抽象實現、而將具體實現過程由子類完成。對於整體步驟很固定,但是某些部分易變,可以將易變的部分抽取出來,供子類實現。
角色:
抽象類:實現模板方法、定義算法骨架
具體類:實現抽象類中的抽象方法,完成特定的算法
2、代碼示例
我們舉一個比較常見的例子:將一個物品裝進冰箱。為了達到這個目的我們一般有如下幾步:
a:打開冰箱門
b:將物品裝進冰箱
c:關上冰箱門
上面的這三步其實就是“將一個物品裝進冰箱”這個算法的骨架。在這個算法中,物品這個字眼很重要,它是抽象的,而不是具體的,對於每個不同的物品,裝入的時候行為可能不同,這一點非常重要。比如:
a:將一塊豬肉放進冰箱--->一塊豬肉這么小,直接放進去
b:將一頭大象放進冰箱--->一頭大象這么大,切碎放進去
上面只是不恰當的舉個例子,只是為了說明:針對與不同物品,放入冰箱的動作(行為)不同。
特別注意:上面物品雖是抽象的,但是我最終想表達的是“將物品裝進冰箱”這個行為是抽象的。
package com.yefengyu.pattern.template; public abstract class AbstractClass { public final void execute() { open(); put(); close(); } private void open() { System.out.println("打開冰箱"); } private void close() { System.out.println("關閉冰箱"); } protected abstract void put(); }
上面的AbstractClass類中的execute方法就是一個算法骨架,它定義了復雜操作的許多步驟,其中需要注意:
(1)AbstractClass是抽象類,因為子類需要繼承某些抽象方法
(2)execute是算法骨架,讓外部調用,所以必須是public,但是又不想讓子類進行重寫,因此使用final關鍵字,如果重寫則導致整個流程混亂。
(3)open和close方法是固定的步驟,定義為私有,不讓子類修改。
(4)put方法是protected 的,保證子類可以重寫,但是其它類無法看到,又是abstract 則子類必須重寫。因為父類AbstractClass無法得知具體的物品該如何放入冰箱,只有靠子類去實現了。
下面來實現兩個子類。
package com.yefengyu.pattern.template; public class Pork extends AbstractClass { @Override protected void put() { System.out.println("將一塊豬肉裝進冰箱:直接裝啊"); } }
package com.yefengyu.pattern.template; public class Elephant extends AbstractClass { @Override protected void put() { System.out.println("將大象裝入冰箱:你必須剁碎再裝入"); } }
這兩個子類雖然在put中都只打印了一句話,但是我們可以想象這里的操作十分復雜,並且流程大不一樣。下面我們來實現客戶端代碼。
package com.yefengyu.pattern.template; public class Client { public static void main(String[] args) { AbstractClass abstractClass = new Pork(); abstractClass.execute(); System.out.println("-------------------"); abstractClass = new Elephant(); abstractClass.execute(); } }
運行結果如下
通過上面的例子,我么可以看出:不同的實現類,重寫的抽象方法的邏輯不同,導致算法執行的結果也不相同,但是算法骨架是沒有改變的。
3、案例剖析
大家應該使用Thread類,在學習的時候,一定都注意到這個問題,我們重寫了run方法,但是線程啟動的時候,為什么使用start方法?
package com.yefengyu.pattern.template; public class MyThread { public static void main(String[] args) { Thread thread = new Thread(){ @Override public void run() { System.out.println("###"); } }; thread.start(); } }
其實Thread類也使用了模板設計模式,只是有些地方有些差異。
總結:
start方法就是算法骨架的入口,它定義算法的骨架;start0()方法是本地方法,該方法最終還是調用了Thread的run方法,具體細節不做介紹。那么我們可以簡單的認為start0()就是run方法,那么這就和模板設計模式非常相似:
start方法:算法框架入口和骨架,和上面的AbstractClass的execute方法對應。
start0方法:可以看作run方法,雖然run方法不是抽象方法,但是也可以復寫,並且一般必須復寫,不然線程沒啥業務,有啥意義呢?
Thread線程的使用start啟動,做了很多其它的事情,也在某個地方調用了run方法,它是線程完整執行的入口,就和上面的AbstractClass的execute方法一樣;如果線程調用run方法啟動,只是和普通方法調用一樣,無法真正啟動一個線程。