模板方法模式
1.定義
定義一個操作中的算法的框架才避,而將一些步驟的實現(xiàn)延遲到子類中粟瞬,使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。
使用模板方法模式制造兩款汽車。定義汽車必須有的特質(zhì):能夠發(fā)動抱虐,鳴笛和停止颈将,不同型號的汽車實現(xiàn)不同梢夯。汽車生產(chǎn)完成后需要對汽車的質(zhì)量進行檢驗,測試汽車的所有功能晴圾。
抽象汽車模型(抽象汽車類)
public abstract class AbstractCar {
// 汽車可以發(fā)動颂砸,發(fā)動方式不同,需要在實現(xiàn)類里實現(xiàn)
public abstract void start();
// 汽車可以鳴笛死姚,但聲音不同人乓,需要在實現(xiàn)類里實現(xiàn)
public abstract void alarm();
// 汽車可以熄火,停止方式也不同都毒,需要在實現(xiàn)類里實現(xiàn)
public abstract void stop();
// 測試汽車的質(zhì)量色罚,操作步驟一樣,先測試啟動账劲,再測試喇叭戳护,最后測試熄火
public void run() {
// 發(fā)動汽車
this.start();
// 按喇叭
this.alarm();
// 停止汽車
this.stop();
}
}
兩種不同型號的汽車(汽車實現(xiàn)類)
public class CarOne extends AbstractCar {
// 發(fā)動C1型號的汽車
public abstract void start() {
System.out.println("手搖啟動C1型號的汽車");
}
// C1型號的汽車鳴笛
public abstract void alarm() {
System.out.println("C1型號的汽車鳴笛滴滴");
}
// 停止C1型號的汽車
public abstract void stop() {
System.out.println("鑰匙關(guān)閉C1型號的汽車");
}
}
public class CarTwo extends AbstractCar {
// 發(fā)動C2型號的汽車
public abstract void start() {
System.out.println("電子啟動C2型號的汽車");
}
// C2型號的汽車鳴笛
public abstract void alarm() {
System.out.println("C1型號的汽車鳴笛嗶嗶");
}
// 停止C2型號的汽車
public abstract void stop() {
System.out.println("電子關(guān)閉C1型號的汽車");
}
}
測試汽車(場景類)
public class Client {
public static void main(String[] args) {
CarOne carOne = new CarOne();
carOne.run();
CarTwo carTwo = new CarTwo();
carTwo.run();
}
}
模板方法模式實現(xiàn)簡單,僅僅使用了java的繼承機制瀑焦。以上述例子為例腌且,其中AbstractCar叫做抽象模板,它的方法分為兩類:
- 基本方法:由子類實現(xiàn)的方法榛瓮,并在模板方法中被調(diào)用铺董。
- 模板方法:數(shù)量不限,一般是一個具體的方法禀晓,也就是定義中所說的算法精续,它實現(xiàn)對基本方法的調(diào)度坝锰,完成固定的邏輯。
CarOne和CarTwo叫做具體模板驻右,實現(xiàn)抽象模板所定義的基本方法什黑。
<font color=#FF0000>注意:</font>抽象模板中的基本方法盡量設(shè)計為
protected
類型,不需要i暴露的屬性或方法盡量不要設(shè)置為protected
類型堪夭,實現(xiàn)類非必要的情況愕把,盡量不擴大父類中的訪問權(quán)限。
2.應用
2.1 優(yōu)點
- 封裝不變部分森爽,擴展可變部分恨豁。把不可變部分封裝到抽象模板中實現(xiàn),可變部分通過繼承來繼續(xù)擴展爬迟。
- 提取公共的代碼橘蜜,便于后期的代碼維護。
- 行為由父類控制付呕,子類只負責實現(xiàn)计福。
2.2 缺點
- 抽象類定義了部分抽象方法,由子類實現(xiàn)徽职,但是子類的執(zhí)行結(jié)果對父類產(chǎn)生了影響象颖,反過來又影響了父類的執(zhí)行結(jié)果,帶來了代碼的閱讀難度姆钉。
2.3 使用場景
- 多個子類有公有的方法说订,且邏輯基本相同。
- 重要潮瓶,復雜的算法設(shè)計為模板方法陶冷,細節(jié)功能由子類實現(xiàn)。
- 重構(gòu)時毯辅,相同的代碼抽取到父類中埂伦,通過<font color=#FF0000>鉤子方法</font>約束其行為。
3.擴展
在上述造車的基礎(chǔ)上實現(xiàn)思恐,C1型車可以控制是否鳴笛沾谜,C2型車不鳴笛。
擴展后的汽車抽象模型
public abstract class AbstractCar {
// 汽車可以發(fā)動壁袄,發(fā)動方式不同,需要在實現(xiàn)類里實現(xiàn)
protected abstract void start();
// 汽車可以鳴笛媚媒,但聲音不同嗜逻,需要在實現(xiàn)類里實現(xiàn)
protected abstract void alarm();
// 汽車可以熄火,停止方式也不同缭召,需要在實現(xiàn)類里實現(xiàn)
protected abstract void stop();
// 測試汽車的質(zhì)量栈顷,操作步驟一樣逆日,先測試啟動,再測試喇叭萄凤,最后測試熄火
public final void run() {
// 發(fā)動汽車
this.start();
if (this.isAlarm()) {
// 按喇叭
this.alarm();
}
// 停止汽車
this.stop();
}
// 鉤子方法室抽,默認喇叭會響
protected boolean isAlarm() {
return true;
}
}
擴展后的汽車實現(xiàn)類
public class CarOne extends AbstractCar {
// C1汽車要可以鳴笛
private boolean alarmFlag = true;
// 發(fā)動C1型號的汽車
protected abstract void start() {
System.out.println("手搖啟動C1型號的汽車");
}
// C1型號的汽車鳴笛
protected abstract void alarm() {
System.out.println("C1型號的汽車鳴笛滴滴");
}
// 停止C1型號的汽車
protected abstract void stop() {
System.out.println("鑰匙關(guān)閉C1型號的汽車");
}
// 是否鳴笛由駕駛員控制
public void setAlarm(boolean isAlarm) {
this.isAlarm = isAlarm;
}
}
public class CarTwo extends AbstractCar {
// 發(fā)動C2型號的汽車
protected abstract void start() {
System.out.println("電子啟動C2型號的汽車");
}
// C2型號的汽車鳴笛
protected abstract void alarm() {
System.out.println("C1型號的汽車鳴笛嗶嗶");
}
// 停止C2型號的汽車
protected abstract void stop() {
System.out.println("電子關(guān)閉C1型號的汽車");
}
// C2汽車不能鳴笛
protected boolean isAlarm() {
return false;
}
}
C1汽車由駕駛員控制是否鳴笛,抽象類中的
isAlarm
方法的返回值影響了run
方法的執(zhí)行結(jié)果靡努,即外界條件的改變會影響到模板方法的執(zhí)行坪圾,該方法就叫做<font color=#FF0000>鉤子方法</font>。