前言
模板方法模式.png模板方法模式就是定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类不改变算法的结构即可重复定义算法的某些特点步骤。
需求
近日,乐视创始人贾跃亭造FF汽车的消息被广而告之。假如你是制造商,贾跃亭让你去制造一个车模型,以便让其观看并修改。
基本实现
定义一个车模抽象类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汽车模型引擎发出声音...");
}
@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();
}
}
运行输出为:
FF汽车模型启动...
FF汽车模型引擎发出声音...
FF汽车模型跑动中奏鸣喇叭...
FF汽车模型停止...
其实run方法的实现应该放在父类上,因为每个车模型都可以有上述方法,故改动如下:
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汽车模型引擎发出声音...");
}
@Override
public void alarm() {
System.out.println("FF汽车模型跑动中奏鸣喇叭...");
}
}
模板方法模式
仔细看上面的写法,发现必然有不妥的地方,比如客户只关心在run的过程中,能够听到或看到这些就行,并不关心具体的实现,只有子类需要关心,所以应该将public改成protected,run方法既然子类都不能修改,是不是可以设置成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汽车模型引擎发出声音...");
}
@Override
protected void alarm() {
System.out.println("FF汽车模型跑动中奏鸣喇叭...");
}
}
大家看这个run方法,它定义了调用其他方法的顺序,并且子类是不能修改的,这个就叫做模板方法。这样模板方法设计模式就已经完成了。
拓展
在上述方法中,这四个方法start、stop、engineBoom、alarm是子类必须实现的,叫做基本方法,基本方法有三种:在抽象类中实现了的叫做具体方法;在抽象类中没有实现,却在子类中实现了的叫做抽象方法;还有一种叫做钩子方法,待会会提到。
这时候,虽然模板方法模式已经将车模造出来了,但是这时候贾跃亭又提了一个需求,要求建造另一个车模FFX,这个车模是不允许发出声音的。那既然贾老板有要求,我们必须做啊:
CarModel 中定义了一个isAlaram钩子方法,默认是返回true:
public abstract class CarModel {
//车模启动
protected abstract void start();
//车模停止
protected abstract void stop();
//车模引擎奏响
protected abstract void engineBoom();
//车模跑动过程中会按喇叭
protected abstract void alarm();
//钩子方法,默认是能够按喇叭的
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汽车模型引擎发出声音...");
}
@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();
}
}
运行输出结果为:
FFX汽车模型启动...
FFX汽车模型引擎发出声音...
FFX汽车模型停止...
这时候贾老板又不乐意了,他觉得喇叭应该由客户去决定,而不是车子决定,于是乎,我们又得做出修改:
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();
//钩子方法,默认是能够按喇叭的
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汽车模型引擎发出声音...");
}
@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();
}
}
运行输出结果为:
FFX汽车模型启动...
FFX汽车模型引擎发出声音...
FFX汽车模型跑动中奏鸣喇叭...
FFX汽车模型停止...
要想使喇叭无效,只需要setAlarm(false)就可以,钩子的作用就是这样的。
模板方法模式在Android中的应用
Activity、Fragment等其实就是利用了模板方法模式,我们写的BaseActivity其实也一定程度上使用了模板方法模式,还有AsyncTask也用到了模板方法模式。
总结
模板方法模式就是在模板方法中按照一个的规则和顺序调用基本方法,具体到我们上面那个例子就是 run 方法按照规定的顺序(先调用 start,然后再调用 engineBoom,再调用alarm,最后调用 stop)调用本类的其他方法。
喜欢本篇博客的简友们,就请来一波点赞,您的每一次关注,将成为我前进的动力,谢谢!作者:zhang_pan
网友评论