開局還是那種圖,各位客官往下看...
張無忌學太極拳,忘記了所有招式,打倒了"玄冥二老",所謂"心中無招"。設計模式可謂招數,如果先學通了各種模式,又忘掉了所有模式而随心所欲,可謂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
關鍵字可以參考:http://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】-- 搞定最近大火的政策模式