設計模式【13】– 模板模式怎麼弄?
開局還是那種圖,各位客官往下看…
張無忌學太極拳,忘記了所有招式,打倒了”玄冥二老”,所謂”心中無招”。設計模式可謂招數,如果先學通了各種模式,又忘掉了所有模式而隨心所欲,可謂OO之最高境界。
模板模式是什麼?
模板模式,同樣是一種行為型模式,也就是關於對象做什麼或者怎麼做的設計模式。模板模式的本質需要定義操作中的演算法的框架,但是有一些步驟,又不需要具體的實現,而是不同的子類各自實現。子類不能修改流程框架,但是部分的步驟可以做訂製化的實現。
主要要解決一個問題:一些通用的方法,但是每一個子類卻都重新寫,冗餘。
比如說,做菜的步驟一般是:洗鍋 --> 炒菜 --> 洗碗
,不同的菜,只是炒菜這一個步驟具體細節是不同的,但是其他步驟確實幾乎一模一樣的,這樣其實整體框架,以及重複的步驟,我們可以抽象到模板中,而不同的細節方法可以開放給每一種菜(具體實現)去訂製。
又比如造房子的時候,很多地方的建造都是一樣的:地基,牆壁,水管等等,但是不同的房子裡面的內部的設計又有所不同。
不使用模板模式
就挑個簡單的例子「炒菜」,如果不使用模板模式的話,糖醋鯉魚:
public class SweetAndSourCarp {
public void cookFood(){
washPan();
cook();
eat();
washDishes();
System.out.println("");
}
private void washPan(){
System.out.print("洗鍋 --> ");
}
private void cook(){
System.out.print("煮糖醋鯉魚 --> ");
}
private void eat(){
System.out.print("吃飯 --> ");
}
private void washDishes(){
System.out.print("洗碗 --> ");
}
}
再弄一個農家小炒肉,需要寫很多相同的方法:
public class ShreddedPorkWithVegetables {
public void cookFood(){
washPan();
cook();
eat();
washDishes();
System.out.println("");
}
private void washPan(){
System.out.print("洗鍋 --> ");
}
private void cook(){
System.out.print("炒農家小炒肉 --> ");
}
private void eat(){
System.out.print("吃飯 --> ");
}
private void washDishes(){
System.out.print("洗碗 --> ");
}
}
測試類如下:
public class Test {
public static void main(String[] args) {
SweetAndSourCarp sweetAndSourCarp = new SweetAndSourCarp();
sweetAndSourCarp.cookFood();
ShreddedPorkWithVegetables shreddedPorkWithVegetables = new ShreddedPorkWithVegetables();
shreddedPorkWithVegetables.cookFood();
}
}
測試結果:
洗鍋 --> 煮糖醋鯉魚 --> 吃飯 --> 洗碗 -->
洗鍋 --> 炒農家小炒肉 --> 吃飯 --> 洗碗 -->
可以看到,整體流程是一樣的,有些步驟一樣,有些步驟不一樣,但是不使用模板模式,需要每個類都重寫一遍方法,即使是通用方法,整個流程都需要自己寫一遍。
使用模板模式優化
如果使用模板模式,那麼我們會抽象出一個抽象類,定義整體的流程,已經固定的步驟,開放需要訂製的方法,讓具體的實現類按照自己的需求來訂製。
定義的抽象類:
public abstract class CookFood {
public final void cookFood() {
washPan();
cook();
eat();
washDishes();
System.out.println("");
}
private final void washPan() {
System.out.print("洗鍋 --> ");
}
public abstract void cook();
private final void eat() {
System.out.print("吃飯 --> ");
}
private final void washDishes() {
System.out.print("洗碗 --> ");
}
}
具體的實現類糖醋鯉魚:
public class SweetAndSourCarp extends CookFood {
@Override
public void cook() {
System.out.print("煮糖醋鯉魚 --> ");
}
}
農家小炒肉:
public class ShreddedPorkWithVegetables extends CookFood {
@Override
public void cook() {
System.out.print("炒農家小炒肉 --> ");
}
}
測試類與前面的一樣,測試結果也一樣,這裡不再重複。
上面的方法中,其實我們只開放了cook()
方法,這就是鉤子方法:
在模板方法模式的父類中,我們可以定義一個方法,它默認不做任何事,子類可以視情況要不要覆蓋它,該方法稱為 」鉤子方法」
鉤子方法是開放的,可以由子類隨意覆蓋,但是像上面的其他方法,我們不希望子類重寫或者覆蓋它,就可以用 final
關鍵字,防止子類重寫模板方法。
模板模式的應用
其實在 JDK
的 Thread
實現中,就是使用了模板模式,我們知道創建執行緒有兩個方式:
- 創建
Thread
類 - 實現
runnable
介面
我們實現的一般是 run()
方法, 但是調用的卻是 start()
方法來啟動執行緒,這個原因就是 start()
方法裡面幫我們調用了run()
方法, run()
方法是開發的方法,我們可以覆蓋重寫它。
Start0()
是一個native
方法,是由 c 語言去實現的,在調用的時候,真正調用了我們的 run()
方法,如果需要跟蹤這個方法需要到 HotSpot
底層去。這裡介紹的目的是讓大家了解,它同樣是使用了模板模式。
private native void start0();
了解 native
關鍵字可以參考://aphysia.cn/archives/native
模板模式的優缺點
模板模式的優點:
- 1、封裝固定的部分,拓展需要訂製修改的部分,符合開閉原則。
- 2、公共的程式碼在父類中,容易維護。
- 3、整個流程由父類把握,調整比較方便。
缺點:
- 1、子類可能會很多,系統複雜度上升。
- 2、子類只有一小部分實現,了解全部方法則需要在父類中閱讀,影響程式碼閱讀。
總結:程式碼該隱藏的複雜細節隱藏起來,開放訂製化部分,優雅!
設計模式系列:
- 設計模式【1】– 單例模式到底幾種寫法?
- 設計模式【1.1】– 你想如何破壞單例模式?
- 設計模式【1.2】– 枚舉式單例有那麼好用么?
- 設計模式【1.3】– 為什麼餓漢式單例是執行緒安全的?
- 設計模式【2】– 簡單工廠模式了解一下?
- 設計模式【2.1】– 簡單工廠模式怎麼演變成工廠方法模式?
- 設計模式【2.2】– 工廠模式怎麼演變成抽象工廠模式?
- 設計模式【3.1】– 淺談代理模式之靜態、動態、cglib代理
- 設計模式【3.2】– JDK動態代理源碼分析有多香?
- 設計模式【3.3】– CGLIB動態代理源碼解讀
- 設計模式【4】– 建造者模式詳解
- 設計模式【5】– 原型模式
- 設計模式【6.1】– 初探適配器模式
- 設計模式【6.2】– 再聊聊適配器模式
- 設計模式【7】– 探索一下橋接模式
- 設計模式【8】– 手工耿教我寫裝飾器模式
- 設計模式【9】– 外觀模式?沒那麼高大上
- 設計模式【10】– 順便看看享元模式
- 設計模式【11】– 搞定組合模式
- 設計模式【12】– 搞定最近大火的策略模式
【作者簡介】:
秦懷,公眾號【秦懷雜貨店】作者,個人網站://aphysia.cn,技術之路不在一時,山高水長,縱使緩慢,馳而不息。