模板方法模式定義
模板方法模式(Template Method Pattern)定義如下:Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.TemplateMethod lets subclasses redefine certain steps of an algorithm without changing the algorithm'sstructure.(定義一個(gè)操作中的算法的框架虚婿,而將一些步驟延遲到子類中腹暖。使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟凫佛。)
模板方法模式非常簡單乓旗,僅僅使用了Java的繼承機(jī)制嫂拴,但它是一個(gè)應(yīng)用非常廣泛的模式,其中AbstractClass叫做抽象模板徒爹,它的方法分為兩類:
- 基本方法:
基本方法也叫基本操作巍实,是由子類實(shí)現(xiàn)的方法,并且在模板方法被調(diào)用东涡。 - 模板方法:
可以又一個(gè)或幾個(gè)冯吓,一般是一個(gè)具體的方法,也就是一個(gè)框架疮跑,實(shí)現(xiàn)對基本方法的調(diào)度组贺,完成固定的邏輯。
模板方法可以配合依賴倒置原則(DIP)使用祖娘,模板方法可以由接口定義失尖,基本方法可由抽象模板實(shí)現(xiàn)。
演示代碼
這次我們的任務(wù)是造車渐苏。先按照最一般的經(jīng)驗(yàn)設(shè)計(jì)類圖
非常簡單的實(shí)現(xiàn)掀潮,悍馬車有兩個(gè)型號,H1和H2整以。按照需求胧辽,只需要悍馬模型,那好我就給你悍馬模型公黑,先寫個(gè)抽象類邑商,然后兩個(gè)不同型號的模型實(shí)現(xiàn)類摄咆,通過簡單的繼承就可以實(shí)現(xiàn)業(yè)務(wù)要求。開始工作人断!
1.抽象悍馬模型
public abstract class HummerModel {
/*
* 首先吭从,這個(gè)模型要能夠被發(fā)動(dòng)起來,別管是手搖發(fā)動(dòng)恶迈,還是電力發(fā)動(dòng)涩金,反正 是要能夠發(fā)動(dòng)起來,那這個(gè)實(shí)現(xiàn)要在實(shí)現(xiàn)類里了
*/
public abstract void start();
// 能發(fā)動(dòng)暇仲,那還要能停下來步做,那才是真本事
public abstract void stop();
// 喇叭會出聲音,是滴滴叫奈附,還是嗶嗶叫
public abstract void alarm();
// 引擎會轟隆隆的響全度,不響那是假的
public abstract void engineBoom();
// 那模型應(yīng)該會跑吧,別管是人推的斥滤,還是電力驅(qū)動(dòng)将鸵,總之要會跑
public abstract void run();
}
在抽象類中,我們定義了悍馬模型都必須具有的特質(zhì):能夠發(fā)動(dòng)佑颇、停止顶掉,喇叭會響,引擎可以轟鳴挑胸,而且還可以停止痒筒。
- H1型號的悍馬
public class HummerH1Model extends HummerModel {
// H1型號的悍馬車鳴笛
public void alarm() {
System.out.println("悍馬H1鳴笛...");
}
// 引擎轟鳴聲
public void engineBoom() {
System.out.println("悍馬H1引擎聲音是這樣在...");
}
// 汽車發(fā)動(dòng)
public void start() {
System.out.println("悍馬H1發(fā)動(dòng)...");
}
// 停車
public void stop() {
System.out.println("悍馬H1停車...");
}
// 開動(dòng)起來
public void run() {
// 先發(fā)動(dòng)汽車
this.start();
// 引擎開始轟鳴
this.engineBoom();
// 然后就開始跑了,跑的過程中遇到一條狗擋路嗜暴,就按喇叭
this.alarm();
// 到達(dá)目的地就停車
this.stop();
}
}
3.H2型號的悍馬
public class HummerH2Model extends HummerModel {
// H2型號的悍馬車鳴笛
public void alarm() {
System.out.println("悍馬H2鳴笛...");
}
// 引擎轟鳴聲
public void engineBoom() {
System.out.println("悍馬H2引擎聲音是這樣在...");
}
// 汽車發(fā)動(dòng)
public void start() {
System.out.println("悍馬H2發(fā)動(dòng)...");
}
// 停車
public void stop() {
System.out.println("悍馬H2停車...");
}
// 開動(dòng)起來
public void run() {
// 先發(fā)動(dòng)汽車
this.start();
// 引擎開始轟鳴
this.engineBoom();
// 然后就開始跑了凸克,跑的過程中遇到一條狗擋路,就按喇叭
this.alarm();
// 到達(dá)目的地就停車
this.stop();
}
}
4.悍馬的模型是造好了闷沥,能不能造出真的呢萎战??試一試舆逃!
public class Client {
public static void main(String[] args) {
HummerH1Model h1 = new HummerH1Model();
h1.start();
h1.engineBoom();
h1.run();
h1.alarm();
h1.run();
h1.stop();
}
}
悍馬H1鳴笛...
悍馬H1發(fā)動(dòng)...
悍馬H1引擎聲音是這樣在...
悍馬H1鳴笛...
悍馬H1停車...
悍馬H1停車...
非常簡單蚂维,那如果我告訴你這就是模板方法模式你會不會很不屑呢?就這模式路狮,太簡單了虫啥,我一直在使用呀!是的奄妨,你經(jīng)常在使用涂籽,但你不知道這是模板方法模式,那些所謂的高手就可以很牛地說:“用模板方法模式就可以實(shí)現(xiàn)”砸抛,你還要很崇拜地看著评雌,哇树枫,牛人,模板方法模式是什么呀景东?這就是模板方法模式砂轻。
注意
- 為了防止惡意的操作,一般模板方法都加上final關(guān)鍵字斤吐,不允許被覆寫搔涝。
- 抽象模板中的基本方法盡量設(shè)計(jì)為protected類型,符合迪米特法則和措,不需要暴露的屬性或方法盡量不要設(shè)置為protected類型庄呈。實(shí)現(xiàn)類若非必要,盡量不要擴(kuò)大父類中的訪問權(quán)限派阱。
模板方法模式的優(yōu)點(diǎn)
- 封裝不變部分抒痒,擴(kuò)展可變部分把認(rèn)為是不變部分的算法封裝到父類實(shí)現(xiàn),而可變部分的則可以通過繼承來繼續(xù)擴(kuò)展颁褂。在悍馬模型例子中,是不是就非常容易擴(kuò)展傀广?例如增加一個(gè)H3型號的悍馬模型颁独,很容易呀,增加一個(gè)子類伪冰,實(shí)現(xiàn)父類的基本方法就可以了誓酒。
- 提取公共部分代碼,便于維護(hù)我們例子中剛剛走過的彎路就是最好的證明贮聂,如果我們不抽取到父類中靠柑,任由這種散亂的代碼發(fā)生,想想后果是什么樣子吓懈?維護(hù)人員為了修正一個(gè)缺陷歼冰,需要到處查找類似的代碼!
- 行為由父類控制耻警,子類實(shí)現(xiàn)基本方法是由子類實(shí)現(xiàn)的隔嫡,因此子類可以通過擴(kuò)展的方式增加相應(yīng)的功能,符合開閉原則甘穿。
模板方法模式的缺點(diǎn)
按照我們的設(shè)計(jì)習(xí)慣腮恩,抽象類負(fù)責(zé)聲明最抽象、最一般的事物屬性和方法温兼,實(shí)現(xiàn)類完成具體的事物屬性和方法秸滴。但是模板方法模式卻顛倒了,抽象類定義了部分抽象方法募判,由子類實(shí)現(xiàn)荡含,子類執(zhí)行的結(jié)果影響了父類的結(jié)果咒唆,也就是子類對父類產(chǎn)生了影響,這在復(fù)雜的項(xiàng)目中内颗,會帶來代碼閱讀的難度钧排,而且也會讓新手產(chǎn)生不適感。
模板方法模式的使用場景
- 多個(gè)子類有公有的方法均澳,并且邏輯基本相同時(shí)恨溜。
- 重要、復(fù)雜的算法找前,可以把核心算法設(shè)計(jì)為模板方法糟袁,周邊的相關(guān)細(xì)節(jié)功能則由各個(gè)
子類實(shí)現(xiàn)。 - 重構(gòu)時(shí)躺盛,模板方法模式是一個(gè)經(jīng)常使用的模式项戴,把相同的代碼抽取到父類中,然后通
過鉤子函數(shù)(見“模板方法模式的擴(kuò)展”)約束其行為槽惫。