前言
模板方法模式就是定義一個操作中的算法框架,而將一些步驟延遲到子類中,使得子類不改變算法的結(jié)構(gòu)即可重復(fù)定義算法的某些特點(diǎn)步驟。
需求
近日,樂視創(chuàng)始人賈躍亭造FF汽車的消息被廣而告之。假如你是制造商,賈躍亭讓你去制造一個車模型,以便讓其觀看并修改。
基本實(shí)現(xiàn)
定義一個車模抽象類CarModel ,里面有車模基本的方法:
public abstract class CarModel {
//車模啟動
public abstract void start();
//車模停止
public abstract void stop();
//車模引擎奏響
public abstract void engineBoom();
//車模跑動過程中會按喇叭
public abstract void alarm();
//車模跑動
public abstract void run();
}
FF汽車模型類CarFFModel :
public class CarFFModel extends CarModel {
@Override
public void start() {
System.out.println("FF汽車模型啟動...");
}
@Override
public void stop() {
System.out.println("FF汽車模型停止...");
}
@Override
public void engineBoom() {
System.out.println("FF汽車模型引擎發(fā)出聲音...");
}
@Override
public void alarm() {
System.out.println("FF汽車模型跑動中奏鳴喇叭...");
}
@Override
public void run() {
this.start();
this.engineBoom();
this.alarm();
this.stop();
}
}
再定義一個客戶端Client,在這里也就是賈躍亭咯:
public class Client {
public static void main(String[] args) {
CarModel ffmodel = new CarFFModel();
ffmodel.run();
}
}
運(yùn)行輸出為:
FF汽車模型啟動...
FF汽車模型引擎發(fā)出聲音...
FF汽車模型跑動中奏鳴喇叭...
FF汽車模型停止...
其實(shí)run方法的實(shí)現(xiàn)應(yīng)該放在父類上,因?yàn)槊總€車模型都可以有上述方法,故改動如下:
CarModel :
public abstract class CarModel {
//車模啟動
public abstract void start();
//車模停止
public abstract void stop();
//車模引擎奏響
public abstract void engineBoom();
//車模跑動過程中會按喇叭
public abstract void alarm();
//車模跑動
public void run() {
this.start();
this.engineBoom();
this.alarm();
this.stop();
}
}
CarFFModel :
public class CarFFModel extends CarModel {
@Override
public void start() {
System.out.println("FF汽車模型啟動...");
}
@Override
public void stop() {
System.out.println("FF汽車模型停止...");
}
@Override
public void engineBoom() {
System.out.println("FF汽車模型引擎發(fā)出聲音...");
}
@Override
public void alarm() {
System.out.println("FF汽車模型跑動中奏鳴喇叭...");
}
}
模板方法模式
仔細(xì)看上面的寫法,發(fā)現(xiàn)必然有不妥的地方,比如客戶只關(guān)心在run的過程中,能夠聽到或看到這些就行,并不關(guān)心具體的實(shí)現(xiàn),只有子類需要關(guān)心,所以應(yīng)該將public改成protected,run方法既然子類都不能修改,是不是可以設(shè)置成final的呢?是滴是滴!
CarModel :
public abstract class CarModel {
//車模啟動
protected abstract void start();
//車模停止
protected abstract void stop();
//車模引擎奏響
protected abstract void engineBoom();
//車模跑動過程中會按喇叭
protected abstract void alarm();
//車模跑動
protected final void run() {
this.start();
this.engineBoom();
this.alarm();
this.stop();
}
}
CarFFModel :
public class CarFFModel extends CarModel {
@Override
protected void start() {
System.out.println("FF汽車模型啟動...");
}
@Override
protected void stop() {
System.out.println("FF汽車模型停止...");
}
@Override
protected void engineBoom() {
System.out.println("FF汽車模型引擎發(fā)出聲音...");
}
@Override
protected void alarm() {
System.out.println("FF汽車模型跑動中奏鳴喇叭...");
}
}
大家看這個run方法,它定義了調(diào)用其他方法的順序,并且子類是不能修改的,這個就叫做模板方法。這樣模板方法設(shè)計(jì)模式就已經(jīng)完成了。
拓展
在上述方法中,這四個方法start、stop、engineBoom、alarm是子類必須實(shí)現(xiàn)的,叫做基本方法,基本方法有三種:在抽象類中實(shí)現(xiàn)了的叫做具體方法;在抽象類中沒有實(shí)現(xiàn),卻在子類中實(shí)現(xiàn)了的叫做抽象方法;還有一種叫做鉤子方法,待會會提到。
這時候,雖然模板方法模式已經(jīng)將車模造出來了,但是這時候賈躍亭又提了一個需求,要求建造另一個車模FFX,這個車模是不允許發(fā)出聲音的。那既然賈老板有要求,我們必須做啊:
CarModel 中定義了一個isAlaram鉤子方法,默認(rèn)是返回true:
public abstract class CarModel {
//車模啟動
protected abstract void start();
//車模停止
protected abstract void stop();
//車模引擎奏響
protected abstract void engineBoom();
//車模跑動過程中會按喇叭
protected abstract void alarm();
//鉤子方法,默認(rèn)是能夠按喇叭的
protected boolean isAlarm() {
return true;
}
//車模跑動
protected final void run() {
this.start();
this.engineBoom();
if (this.isAlarm()) {
this.alarm();
}
this.stop();
}
}
CarFFXModel :
public class CarFFXModel extends CarModel {
@Override
protected void start() {
System.out.println("FFX汽車模型啟動...");
}
@Override
protected void stop() {
System.out.println("FFX汽車模型停止...");
}
@Override
protected void engineBoom() {
System.out.println("FFX汽車模型引擎發(fā)出聲音...");
}
@Override
protected void alarm() {
System.out.println("FFX汽車模型跑動中奏鳴喇叭...");
}
@Override
protected boolean isAlarm() {
return false;
}
}
Client:
public class Client {
public static void main(String[] args) {
CarModel ffmodel = new CarFFXModel();
ffmodel.run();
}
}
運(yùn)行輸出結(jié)果為:
FFX汽車模型啟動...
FFX汽車模型引擎發(fā)出聲音...
FFX汽車模型停止...
這時候賈老板又不樂意了,他覺得喇叭應(yīng)該由客戶去決定,而不是車子決定,于是乎,我們又得做出修改:
CarModel :
public abstract class CarModel {
private boolean isAlarm = true; //是否要響喇叭
//車模啟動
protected abstract void start();
//車模停止
protected abstract void stop();
//車模引擎奏響
protected abstract void engineBoom();
//車模跑動過程中會按喇叭
protected abstract void alarm();
//鉤子方法,默認(rèn)是能夠按喇叭的
protected boolean isAlarm() {
return this.isAlarm;
}
//由客戶去決定
public void setAlarm(boolean isAlarm) {
this.isAlarm = isAlarm;
}
//車模跑動
protected final void run() {
this.start();
this.engineBoom();
if (this.isAlarm()) {
this.alarm();
}
this.stop();
}
}
CarFFXModel :
public class CarFFXModel extends CarModel {
@Override
protected void start() {
System.out.println("FFX汽車模型啟動...");
}
@Override
protected void stop() {
System.out.println("FFX汽車模型停止...");
}
@Override
protected void engineBoom() {
System.out.println("FFX汽車模型引擎發(fā)出聲音...");
}
@Override
protected void alarm() {
System.out.println("FFX汽車模型跑動中奏鳴喇叭...");
}
}
Client:
public class Client {
public static void main(String[] args) {
CarModel ffmodel = new CarFFXModel();
ffmodel.setAlarm(true);
ffmodel.run();
}
}
運(yùn)行輸出結(jié)果為:
FFX汽車模型啟動...
FFX汽車模型引擎發(fā)出聲音...
FFX汽車模型跑動中奏鳴喇叭...
FFX汽車模型停止...
要想使喇叭無效,只需要setAlarm(false)就可以,鉤子的作用就是這樣的。
模板方法模式在Android中的應(yīng)用
Activity、Fragment等其實(shí)就是利用了模板方法模式,我們寫的BaseActivity其實(shí)也一定程度上使用了模板方法模式,還有AsyncTask也用到了模板方法模式。
總結(jié)
模板方法模式就是在模板方法中按照一個的規(guī)則和順序調(diào)用基本方法,具體到我們上面那個例子就是 run 方法按照規(guī)定的順序(先調(diào)用 start,然后再調(diào)用 engineBoom,再調(diào)用alarm,最后調(diào)用 stop)調(diào)用本類的其他方法。
喜歡本篇博客的簡友們,就請來一波點(diǎn)贊,您的每一次關(guān)注,將成為我前進(jìn)的動力,謝謝!作者:zhang_pan