第10章 模板方法模式
10.1 辉煌工程——制造悍马
周三,9:00,我刚刚坐到位置上,打开电脑准备开始干活。
“小三,小三,叫一下其他同事,到会议室开会”,老大跑过来吼,带着坏笑。还没等大家坐稳,老大就开讲了:
“告诉大家一个好消息,昨天终于把××模型公司的口子打开了,要我们做悍马模型,虽然是第一个车辆模型,但是我们有能力、有信心做好,我们一定要……”(中间省略20分钟的讲话,如果你听过领导人的讲话,这个你应该能够续上)
动员工作做完了,那就开始压任务了。“这次时间是非常紧张的,只有一个星期的时间,小三,你负责在一个星期的时间把这批10万车模(注:车模是车辆模型的意思,不是香车美女那个车模)建设完成……”
“一个星期?这个……是真做不完,要做分析,做模板,做测试,还要考虑扩展性、稳定性、健壮性等,时间实在是太少了。”还没等老大说完,我就急了,再不急我的小命就折在上面了!
“那这样,只做最基本的实现,不考虑太多的问题,怎么样?”老大又把我弹回去了。
“只作基本实现?那……”
唉,领导已经布置任务了,那就开始拼命地做吧。然后就开始准备动手做,在做之前先介绍一下我们公司的背景,我们公司是做模型生产的,做过桥梁模型、建筑模型、机械模型,甚至是一些政府、军事的机密模型,这个不能细说,绝密。公司的主要业务就是把实物按照一定的比例缩小或放大,用于试验、分析、量化或者是销售,等等,上面提到的××模型公司是专门销售车辆模型的公司,自己没有生产企业,全部是代工。我们公司是第一次从××模型公司接单,那我怎么着也要把活干好,可时间有限,任务量又巨大,怎么办?
既然领导都说了,不考虑扩展性,那好办,先按照最一般的经验设计类图,如图10-1所示。
![](https://box.kancloud.cn/2016-08-14_57b003614c644.jpg)
图10-1 悍马车模型最一般的类图
非常简单的实现,悍马车有两个型号,H1和H2。按照需求,只需要悍马模型,那好我就给你悍马模型,先写个抽象类,然后两个不同型号的模型实现类,通过简单的继承就可以实现业务要求。我们先从抽象类开始编写,抽象悍马模型如代码清单10-1所示。
代码清单10-1 抽象悍马模型
public abstract class HummerModel {
/*
* 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正
* 是要能够发动起来,那这个实现要在实现类里了
*/
public abstract void start();
//能发动,还要能停下来,那才是真本事
public abstract void stop();
//喇叭会出声音,是滴滴叫,还是哔哔叫
public abstract void alarm();
//引擎会轰隆隆地响,不响那是假的
public abstract void engineBoom();
//那模型应该会跑吧,别管是人推的,还是电力驱动的,总之要会跑
public abstract void run();
}
在抽象类中,我们定义了悍马模型都必须具有的特质:能够发动、停止,喇叭会响,引擎可以轰鸣,而且还可以停止。但是每个型号的悍马实现是不同的,H1型号的悍马如代码清单10-2所示。
代码清单10-2 H1型号悍马模型
public class HummerH1Model extends HummerModel {
//H1型号的悍马车鸣笛
public void alarm() {
System.out.println("悍马H1鸣笛...");
}
//引擎轰鸣声
public void engineBoom() {
System.out.println("悍马H1引擎声音是这样的...");
}
//汽车发动
public void start() {
System.out.println("悍马H1发动...");
}
//停车
public void stop() {
System.out.println("悍马H1停车...");
}
//开动起来
public void run(){
//先发动汽车
this.start();
//引擎开始轰鸣
this.engineBoom();
//然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm();
//到达目的地就停车
this.stop();
}
}
大家注意看run()方法,这是一个汇总的方法,一个模型生产成功了,总要拿给客户检测吧,怎么检测?“是骡子是马,拉出去溜溜”,这就是一种检验方法,让它跑起来!通过run()这样的方法,把模型的所有功能都测试到了。
H2型号悍马如代码清单10-3所示。
代码清单10-3 H2型号悍马模型
public class HummerH2Model extends HummerModel {
//H2型号的悍马车鸣笛
public void alarm() {
System.out.println("悍马H2鸣笛...");
}
//引擎轰鸣声
public void engineBoom() {
System.out.println("悍马H2引擎声音是这样在...");
}
//汽车发动
public void start() {
System.out.println("悍马H2发动...");
}
//停车
public void stop() {
System.out.println("悍马H2停车...");
}
//开动起来
public void run(){
//先发动汽车
this.start();
//引擎开始轰鸣
this.engineBoom();
//然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm();
//到达目的地就停车
this.stop();
}
}
好了,程序编写到这里,已经发现问题了,两个实现类的run()方法都是完全相同的,那这个run()方法的实现应该出现在抽象类,不应该在实现类上,抽象是所有子类的共性封装。
注意 在软件开发过程中,如果相同的一段代码复制过两次,就需要对设计产生怀疑,架构师要明确地说明为什么相同的逻辑要出现两次或更多次。
好,问题发现了,我们就需要马上更改,修改后的类图如图10-2所示。
![](https://box.kancloud.cn/2016-08-14_57b0036161d71.jpg)
图10-2 修改后的悍马车模类图
注意,抽象类HummerModel中的run()方法,由抽象方法变更为实现方法,其源代码如代码清单10-4所示。
代码清单10-4 修改后的抽象悍马模型
public abstract class HummerModel {
/*
* 首先,这个模型要能发动起来,别管是手摇发动,还是电力发动,反正
* 是要能够发动起来,那这个实现要在实现类里了
*/
public abstract void start();
//能发动,还要能停下来,那才是真本事
public abstract void stop();
//喇叭会出声音,是滴滴叫,还是哔哔叫
public abstract void alarm();
//引擎会轰隆隆地响,不响那是假的
public abstract void engineBoom();
//那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
public void run(){
//先发动汽车
this.start();
//引擎开始轰鸣
this.engineBoom();
//然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm();
//到达目的地就停车
this.stop();
}
}
在抽象的悍马模型上已经定义了run()方法的执行规则,先启动,然后引擎立刻轰鸣,中间还要按一下喇叭,制造点噪声(要不就不是名车了)。然后停车,它的两个具体实现类就不需要实现run()方法了,只要把代码清单10-2、代码清单10-3上的run()方法删除即可,不再赘述代码。
场景类实现的任务就是把生产出的模型展现给客户,其源代码如代码清单10-5所示。
代码清单10-5 场景类
public class Client {
public static void main(String[] args) {
//XX公司要H1型号的悍马
HummerModel h1 = new HummerH1Model();
//H1模型演示
h1.run();
}
}
运行结果如下所示。
悍马H1发动...
悍马H1引擎声音是这样的...
悍马H1鸣笛...
悍马H1停车...
目前客户只要看H1型号的悍马车,没问题,生产出来,同时可以运行起来给他看看。非常简单,那如果我告诉你这就是模板方法模式你会不会很不屑呢?就这模式,太简单了,我一直在使用呀!是的,你经常在使用,但你不知道这是模板方法模式,那些所谓的高手就可以很牛地说:“用模板方法模式就可以实现”,你还要很崇拜地看着,哇,牛人,模板方法模式是什么呀?这就是模板方法模式。
- 前言
- 第一部分 大旗不挥,谁敢冲锋——6大设计原则全新解读
- 第1章 单一职责原则
- 1.2 绝杀技,打破你的传统思维
- 1.3 我单纯,所以我快乐
- 1.4 最佳实践
- 第2章 里氏替换原则
- 2.2 纠纷不断,规则压制
- 2.3 最佳实践
- 第3章 依赖倒置原则
- 3.2 言而无信,你太需要契约
- 3.3 依赖的三种写法
- 3.4 最佳实践
- 第4章 接口隔离原则
- 4.2 美女何其多,观点各不同
- 4.3 保证接口的纯洁性
- 4.4 最佳实践
- 第5章 迪米特法则
- 5.2 我的知识你知道得越少越好
- 5.3 最佳实践
- 第6章 开闭原则
- 6.2 开闭原则的庐山真面目
- 6.3 为什么要采用开闭原则
- 6.4 如何使用开闭原则
- 6.5 最佳实践
- 第二部分 真刀实枪 ——23种设计模式完美演绎
- 第7章 单例模式
- 7.2 单例模式的定义
- 7.3 单例模式的应用
- 7.4 单例模式的扩展
- 7.5 最佳实践
- 第8章 工厂方法模式
- 8.2 工厂方法模式的定义
- 8.3 工厂方法模式的应用
- 8.4 工厂方法模式的扩展
- 8.5 最佳实践
- 第9章 抽象工厂模式
- 9.2 抽象工厂模式的定义
- 9.3 抽象工厂模式的应用
- 9.4 最佳实践
- 第10章 模板方法模式
- 10.2 模板方法模式的定义
- 10.3 模板方法模式的应用
- 10.4 模板方法模式的扩展
- 10.5 最佳实践
- 第11章 建造者模式
- 11.2 建造者模式的定义
- 11.3 建造者模式的应用
- 11.4 建造者模式的扩展
- 11.5 最佳实践
- 第12章 代理模式
- 12.2 代理模式的定义
- 12.3 代理模式的应用
- 12.4 代理模式的扩展
- 12.5 最佳实践
- 第13章 原型模式
- 13.2 原型模式的定义
- 13.3 原型模式的应用
- 13.4 原型模式的注意事项
- 13.5 最佳实践
- 第14章 中介者模式
- 14.2 中介者模式的定义
- 14.3 中介者模式的应用
- 14.4 中介者模式的实际应用
- 14.5 最佳实践
- 第15章 命令模式
- 15.2 命令模式的定义
- 15.3 命令模式的应用
- 15.4 命令模式的扩展
- 15.5 最佳实践
- 第16章 责任链模式
- 16.2 责任链模式的定义
- 16.3 责任链模式的应用
- 16.4 最佳实践
- 第17章 装饰模式
- 17.2 装饰模式的定义
- 17.3 装饰模式应用
- 17.4 最佳实践
- 第18章 策略模式
- 18.2 策略模式的定义
- 18.3 策略模式的应用
- 18.4 策略模式的扩展
- 18.5 最佳实践
- 第19章 适配器模式
- 19.2 适配器模式的定义
- 19.3 适配器模式的应用
- 19.4 适配器模式的扩展
- 19.5 最佳实践
- 第20章 迭代器模式
- 20.2 迭代器模式的定义
- 20.3 迭代器模式的应用
- 20.4 最佳实践
- 第21章 组合模式
- 21.2 组合模式的定义
- 21.3 组合模式的应用
- 21.4 组合模式的扩展
- 21.5 最佳实践
- 第22章 观察者模式
- 22.2 观察者模式的定义
- 22.3 观察者模式的应用
- 22.4 观察者模式的扩展
- 22.5 最佳实践
- 第23章 门面模式
- 23.2 门面模式的定义
- 23.3 门面模式的应用
- 23.4 门面模式的注意事项
- 23.5 最佳实践
- 第24章 备忘录模式
- 24.2 备忘录模式的定义
- 24.3 备忘录模式的应用
- 24.4 备忘录模式的扩展
- 24.5 最佳实践
- 第25章 访问者模式
- 25.2 访问者模式的定义
- 25.3 访问者模式的应用
- 25.4 访问者模式的扩展
- 25.5 最佳实践
- 第26章 状态模式
- 26.2 状态模式的定义
- 26.3 状态模式的应用
- 第27章 解释器模式
- 27.2 解释器模式的定义
- 27.3 解释器模式的应用
- 27.4 最佳实践
- 第28章 享元模式
- 28.2 享元模式的定义
- 28.3 享元模式的应用
- 28.4 享元模式的扩展
- 28.5 最佳实践
- 第29章 桥梁模式
- 29.2 桥梁模式的定义
- 29.3 桥梁模式的应用
- 29.4 最佳实践
- 第三部分 谁的地盘谁做主 ——设计模式PK
- 第30章 创建类模式大PK
- 30.1 工厂方法模式VS建造者模式
- 30.2 抽象工厂模式VS建造者模式
- 第31章 结构类模式大PK
- 31.1 代理模式VS装饰模式
- 31.2 装饰模式VS适配器模式
- 第32章 行为类模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS状态模式
- 32.3 观察者模式VS责任链模式
- 第33章 跨战区PK
- 33.1 策略模式VS桥梁模式
- 33.2 门面模式VS中介者模式
- 33.3 包装模式群PK
- 第四部分 完美世界 ——设计模式混编
- 第34章 命令模式+责任链模式
- 34.2 混编小结
- 第35章 工厂方法模式+策略模式
- 35.2 混编小结
- 第36章 观察者模式+中介者模式
- 36.2 混编小结
- 第五部分 扩展篇
- 第37章 MVC框架
- 37.2 最佳实践
- 第38章 新模式
- 38.1 规格模式
- 38.2 对象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空对象模式
- 附录 23种设计模式彩图