24种设计模式--模版方法模式【Template Method Pattern】
周三,9:00,我刚刚坐到位置,打开电脑准备开始干活。“小三,小三,叫一下其它同事,到会议室,开会”老大跑过来吼,带着淫笑。还不等大家坐稳,老大就开讲了,“告诉大家一个好消息,昨天终于把牛叉模型公司的口子打开了,要我们做悍马模型,虽然是第一个车辆模型,但是我们有能力,有信心做好,我们一定要…(中间省略 20 分钟的讲话,如果你听过领导人的讲话,这个你应该能够续上)”动员工作做完了,那就开始压任务了,“这次时间是非常紧张的,只有一个星期的时间,小三,你负责在一个星期的时间把这批 10 万车模(注:车模是车辆模型的意思,不是香车美女那个车模)建设完成…”“一个星期?这个…,是真做不完,要做分析,做模板,做测试,还要考虑扩展性、稳定性、健壮性等,时间实在是太少了”还没等老大说完,我就急了,再不急我的小命就折在上面了!“那这样,你只做实现,不考虑使用设计模式,扩展性等都不用考虑”老大又把我压回去了。“不考虑设计模式?那…”哎,领导已经布置任务了,那就开始死命的做吧,命苦不能怨政府,点背不能怪社会呀,然后就开始准备动手做,在做之前先介绍一下我们公司的背景,我们公是做模型生产的,做过桥梁模型、建筑模型、机械模型,甚至是一些政府、军事的机密模型,这个不能说,就是把真实的实物按照一定的比例缩小或放大,用于试验、分析、量化或者是销售等等,上面提到的牛叉模型公司专门销售车辆模型的公司,自己不生产,我们公司是第一次从牛叉模型公司接单,那我怎么着也要把活干好,可时间很紧张呀,怎么办?既然领导都说了,不考虑扩展性,那好办,我先设计个类图:
package com.pattern.templateMethod; /**
* 悍马车辆模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public abstract class HummerModel { /**
* 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了
*/
protected abstract void start(); /**
* 能发动,那还要能停下来,那才是真本事
*/
protected abstract void stop(); /**
* 喇叭会出声音,是滴滴叫,还是哗哗叫
*/
protected abstract void alarm(); /**
* 引擎会轰隆隆的响,不响那是假的
*/
protected abstract void engineBoom(); /**
* 那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
*/
public abstract void run(); } H1 型号悍马的定义如下:
package com.pattern.templateMethod; /**
* 悍马车是每个越野者的最爱,其中H1最接近军用系列
* @author http://www.cnblogs.com/initial-road/
*
*/
public class HummerH1Model extends HummerModel { @Override
public void alarm() {
System.out.println("悍马H1鸣笛...");
} @Override
public void engineBoom() {
System.out.println("悍马H1引擎声音...");
} @Override
public void start() {
System.out.println("悍马H1发动...");
} @Override
public void stop() {
System.out.println("悍马H1停车...");
} /**
* 这个方法是很有意思的,它要跑,那肯定是启动,停止了等,也就是有调用其他方法
*/
@Override
public void run(){ //先发动汽车
this.start(); //引擎开始轰鸣
this.engineBoom(); //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm(); //达到目的地就停车
this.stop(); } } 然后看悍马 H2 型号的实现:
package com.pattern.templateMethod; /**
* H1和H2有什么差别,还真不知道,真没接触过悍马
* @author http://www.cnblogs.com/initial-road/
*
*/
public class HummerH2Model extends HummerModel { @Override
public void alarm() {
System.out.println("悍马H2鸣笛...");
} @Override
public void engineBoom() {
System.out.println("悍马H2引擎声音是这样的...");
} @Override
public void start() {
System.out.println("悍马H2发动...");
} @Override
public void stop() {
System.out.println("悍马H2停车...");
} /**
* H2要跑,那肯定要启动,停止了等,也就是要调用其他方法
*/
@Override
public void run(){ //先发动汽车
this.start(); //引擎开始轰鸣
this.engineBoom(); //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm(); //达到目的地就停车
this.stop(); } }
然后程序写到这里,你就看到问题了,run 方法的实现应该在抽象类上,不应该在实现类上,好,我们修改一类图和实现:
就把 run 方法放到了抽象类中,那代码也相应的改变一下,先看 HummerModel.java:
package com.pattern.templateMethod; /**
* 悍马车辆模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public abstract class HummerModel { /**
* 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了
*/
protected abstract void start(); /**
* 能发动,那还要能停下来,那才是真本事
*/
protected abstract void stop(); /**
* 喇叭会出声音,是滴滴叫,还是哗哗叫
*/
protected abstract void alarm(); /**
* 引擎会轰隆隆的响,不响那是假的
*/
protected abstract void engineBoom(); /**
* 那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
*/
public void run(){ //先发动汽车
this.start(); //引擎开始轰鸣
this.engineBoom(); //然后开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm(); //达到目的地就停车
this.stop(); } } 下面是 HummerH1Model.java 程序清单:
package com.pattern.templateMethod; /**
* 悍马车是每个越野者的最爱,其中H1最接近军用系列
* @author http://www.cnblogs.com/initial-road/
*
*/
public class HummerH1Model extends HummerModel { @Override
public void alarm() {
System.out.println("悍马H1鸣笛...");
} @Override
public void engineBoom() {
if(this.isAlarm())
System.out.println("悍马H1引擎声音...");
} @Override
public void start() {
System.out.println("悍马H1发动...");
} @Override
public void stop() {
System.out.println("悍马H1停车...");
} } 下面是 HummerH2Model.java 的程序清单:
package com.pattern.templateMethod; /**
* H1和H2有什么差别,还真不知道,真没接触过悍马
* @author http://www.cnblogs.com/initial-road/
*
*/
public class HummerH2Model extends HummerModel { @Override
public void alarm() {
if(this.isAlarm())
System.out.println("悍马H2鸣笛...");
} @Override
public void engineBoom() {
System.out.println("悍马H2引擎声音是这样的...");
} @Override
public void start() {
System.out.println("悍马H2发动...");
} @Override
public void stop() {
System.out.println("悍马H2停车...");
} }
类图修改完毕了,程序也该好了,提交给老大,老大一看,挺好,就开始生产了,并提交给客户使用了,那客户是如何使用的呢?类图上增加一个 Client 类,就是客户,我们这个是用 main 函数来代替他使用,类图如下:
然后看增加的 Client.java 程序,非常的简单:
package com.pattern.templateMethod; /**
* 客户开始使用这个模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class Client { public static void main(String[] args) { //客户开着H1型号,出去遛弯了
HummerH1Model h1 = new HummerH1Model();
//驱车跑起来了
h1.run(); //客户开着H2型号,出去玩耍了
HummerModel h2 = new HummerH2Model();
h2.run();
} }
非常非常的简单,那如果我告诉这就是模板方法模式你会不会很不屑呢?就这模式,太简单了,我一直在使用呀,是的,你经常在使用,但你不知道这是模板方法模式,那些所谓的高手就可以很牛 X 的说“用模板方法模式就可以实现…”,你还要很崇拜的看着,哇,牛人,模板方法模式是什么呀?
然后我们继续回顾我们这个模型,回头一想,不对呀,需求分析的有点问题,客户要关心模型的启动,停止,鸣笛,引擎声音吗?他只要在 run 的过程中,听到或看都成了呀,暴露那么多的方法干啥?好了,我们重新修改一下类图:
把抽象类上的四个方法设置为 protected 访问权限,好了,既然客户不关心这几个方法,而且这四个方法都是由子类来实现的,那就设置成 protected 模式。咦~,那还有个缺陷,run 方法既然子类都不修改,那是不是可以设置成 final 类型呢?是滴是滴,类图如下:
好了,这才是模板方法模式,就是这个样子,我们只要修改抽象类代码就可以了,HummerModel.java程序清单如下:
package com.pattern.templateMethod; /**
* 悍马车辆模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public abstract class HummerModel { /**
* 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了
*/
protected abstract void start(); /**
* 能发动,那还要能停下来,那才是真本事
*/
protected abstract void stop(); /**
* 喇叭会出声音,是滴滴叫,还是哗哗叫
*/
protected abstract void alarm(); /**
* 引擎会轰隆隆的响,不响那是假的
*/
protected abstract void engineBoom(); /**
* 那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
*/
final public void run(){ //先发动汽车
this.start(); //引擎开始轰鸣
this.engineBoom(); //然后开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm(); //达到目的地就停车
this.stop(); } }
其他的子类都不用修改(如果要修改,就是把四个方法的访问权限由 public 修改 protected),大家请看这个 run 方法,他定义了调用其他方法的顺序,并且子类是不能修改的,这个叫做模板方法; start、 stop、alarm、engineBoom 这四个方法是子类必须实现的,而且这四个方法的修改对应了不同的类,这个叫做基本方法,基本方法又分为三种:在抽象类中实现了的基本方法叫做具体方法;在抽象类中没有实现,在子类中实现了叫做抽象方法,我们这四个基本方法都是抽象方法,由子类来实现的;还有一种叫做钩子方法,这个等会讲。到目前为止,这两个模型都稳定的运行,突然有一天,老大又找到了我,“客户提出新要求了,那个喇叭想让它响就响,你看你设计的模型,车子一启动,喇叭就狂响,赶快修改一下”,确实是设计缺陷,呵呵,不过是我故意的,那我们怎么修改呢?看修改后的类图:
增加一个方法,isAlarm(),喇嘛要不要响,这就是钩子方法(Hook Method),那我们只要修改一下抽象类就可以了:
package com.pattern.templateMethod; /**
* 悍马车辆模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public abstract class HummerModel { /**
* 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了
*/
protected abstract void start(); /**
* 能发动,那还要能停下来,那才是真本事
*/
protected abstract void stop(); /**
* 喇叭会出声音,是滴滴叫,还是哗哗叫
*/
protected abstract void alarm(); /**
* 引擎会轰隆隆的响,不响那是假的
*/
protected abstract void engineBoom(); /**
* 那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
*/
final public void run(){ //先发动汽车
this.start(); //引擎开始轰鸣
this.engineBoom(); //然后开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm(); //达到目的地就停车
this.stop(); } /**
* 钩子方法,默认喇叭是会响的
*/
protected boolean isAlarm(){
return true;
} }
钩子方法模式是由抽象类来实现的,子类可以重写的,H2 型号的悍马是不会叫的,喇叭是个摆设,看HummerH2Model.java 代码:
package com.pattern.templateMethod; /**
* H1和H2有什么差别,还真不知道,真没接触过悍马
* @author http://www.cnblogs.com/initial-road/
*
*/
public class HummerH2Model extends HummerModel { @Override
public void alarm() {
if(this.isAlarm())
System.out.println("悍马H2鸣笛...");
} @Override
public void engineBoom() {
System.out.println("悍马H2引擎声音是这样的...");
} @Override
public void start() {
System.out.println("悍马H2发动...");
} @Override
public void stop() {
System.out.println("悍马H2停车...");
} //默认没有喇叭的
@Override
protected boolean isAlarm(){
return false;
} }
那 H2 型号的模型都没有喇叭,就是按了喇叭也没有声音,那客户端这边的调用没有任何修改,出来的结果就不同,我们先看 Client.java 程序:
package com.pattern.templateMethod; /**
* 客户开始使用这个模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class Client { public static void main(String[] args) { HummerModel h2 = new HummerH2Model();
//客户开着H2型号跑起来了
h2.run();
} }
那 H1 又有所不同了,它的喇叭要不要响是由客户来决定,其实在类图上已经标明了 setAlarm 这个方法,我们看 HummerH1Model.java 的代码:
package com.pattern.templateMethod; /**
* 悍马车是每个越野者的最爱,其中H1最接近军用系列
* @author http://www.cnblogs.com/initial-road/
*
*/
public class HummerH1Model extends HummerModel {
protected boolean alarmFlag = true; //是否要响喇叭 @Override
public void alarm() {
System.out.println("悍马H1鸣笛...");
} @Override
public void engineBoom() {
if(this.isAlarm())
System.out.println("悍马H1引擎声音...");
} @Override
public void start() {
System.out.println("悍马H1发动...");
} @Override
public void stop() {
System.out.println("悍马H1停车...");
} public void setAlarmFlag(boolean alarmFlag) {
this.alarmFlag = alarmFlag;
} @Override
protected boolean isAlarm() {
return this.alarmFlag;
} }
这段代码呢修改了两个地方,一是重写了父类的 isAlarm()方法,一是增加了一个 setAlarm 方法,由调用者去决定是否要这个功能,也就是喇叭要不要滴滴答答的响,哈哈,那我们看看 Client.java 的修改:
package com.pattern.templateMethod; /**
* 客户开始使用这个模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class Client { public static void main(String[] args) { //客户开着H1型号,出去遛弯了
HummerH1Model h1 = new HummerH1Model();
h1.setAlarmFlag(true);
//驱车跑起来了
h1.run(); } }
看到没,这个模型 run 起来就有声音了,那当然把 h1.setAlarm(false)运行起来喇叭就没有声音了,钩子方法的作用就是这样滴。那我们总结一下模板方法模式,模板方法模式就是在模板方法中按照一个的规则和顺序调用基本方法,具体到我们上面那个例子就是 run 方法按照规定的顺序(先调用 start,然后再调用 engineBoom,再调用alarm,最后调用 stop)调用本类的其他方法,并且由 isAlarm 方法的返回值确定 run 中的执行顺序变更,通用类图如下:
其中 TemplateMethod 就是模板方法,operation1 和 operation2 就是基本方法,模板方法是通过汇总或排序基本方法而产生的结果集。模板方法在一些开源框架中应用很多,它提供了一个抽象类,然后开源框架写了一堆子类,在《XXX In Action》中就说明了,如果你需要扩展功能,可以继承了这个抽象类,然后修改 protected 方法,再然后就是调用一个类似 execute 方法,就完成你的扩展开发,确实是一种简单的模式。
24种设计模式--模版方法模式【Template Method Pattern】的更多相关文章
- 二十四种设计模式:模板方法模式(Template Method Pattern)
模板方法模式(Template Method Pattern) 介绍定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.Template Method使得子类可以不改变一个算法的结构即可重定义该算法 ...
- 乐在其中设计模式(C#) - 模板方法模式(Template Method Pattern)
原文:乐在其中设计模式(C#) - 模板方法模式(Template Method Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 模板方法模式(Template Method ...
- 设计模式 笔记 模版方法模式 Template Method
//---------------------------15/04/28---------------------------- //TemplateMethod 模版方法模式----类行为型模式 ...
- 24种设计模式--工厂方法模式【Factory Method Pattern】
女娲补天的故事大家都听说过吧,今天不说这个,说女娲创造人的故事,可不是“造人”的工作,这个词被现代人滥用了. 这个故事是说,女娲在补了天后,下到凡间一看,哇塞,风景太优美了,天空是湛蓝的,水是清澈的, ...
- C#设计模式——工厂方法模式(Factory Method Pattern)
一.概述在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.如何应对这种变化?如何提供一种封装机制来隔离出“这个易变对象 ...
- 模版方法模式(Template Method)
1.概念 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板.它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行.这种类型的设计模式属于行为型 ...
- 24种设计模式--中介者模式【Mediator Pattern】
各位好,大家都是来自五湖四海,都要生存,于是都找了个靠山——公司,给你发薪水的地方,那公司就要想尽办法盈利赚钱,盈利方法则不尽相同,但是作为公司都有相同三个环节:采购.销售和库存,这个怎么说呢?比如一 ...
- 24种设计模式--多例模式【Multition Pattern】
这种情况有没有?有!大点声,有没有?有,是,确实有,就出现在明朝,那三国期间的算不算,不算,各自称帝,各有各的地盘,国号不同.大家还 记得那首诗<石灰吟>吗?作者是谁?于谦,他是被谁杀死的 ...
- .NET设计模式(16):模版方法(Template Method)(转)
摘要:Template Method模式是比较简单的设计模式之一,但它却是代码复用的一项基本的技术,在类库中尤其重要. 主要内容 1.概述 2.Template Method解说 3..NET中的Te ...
随机推荐
- (DT系列三)系统启动时, dts 是怎么被加载的
一,主要问题:系统在启动的时候,是怎么加载 dts的:Lk,kernel中都应调查. 二:参考文字dts加载流程如下图所示: 启动过程中,bootloader(默认是bootable/bootload ...
- Web开发,如何从小工到专家
最近在研读关于“整体性学习”的一些东西,收获颇丰. 整体性学习强调的东西有三样:结构.模型.与高速通道.特别是关于结构的篇章: 理解是什么?理解就是结构高度发达完善的结果. 是不是有些学科你可以轻松“ ...
- HDU 2298 Toxophily
题目: Description The recreation center of WHU ACM Team has indoor billiards, Ping Pang, chess and bri ...
- POJ 1325、ZOJ 1364、HDU 1150 Machine Schedule - from lanshui_Yang
Problem Description As we all know, machine scheduling is a very classical problem in computer scien ...
- 深入了解当前ETL中用到的一些基本技术
数据集成是把不同来源.格式和特点的数据在逻辑上或物理上有机地集中,从而为企业提供全面的数据共享,是企业商务智能.数据仓库系统的重要组成部分.ETL是企业数据集成的概念出发,简要分析了当前ETL中用到的 ...
- careercup-链表 2.2
2.2 实现一个算法,找到单链表中倒数第k个节点. 这道题的考点在于我们怎么在一个单链表中找到倒数第n个元素? 由于是单链表,所以我们没办法从最后一个元素数起,然后数n个得到答案. 但这种最直观的思路 ...
- mysql 超时设置
在Mysql的默认设置中,如果一个数据库连接超过8小时没有使用(闲置8小时,即 28800s),mysql server将主动断开这条连接,后续在该连接上进行的查询操作都将失败,将 出现: ...
- ccrendertexture
int bgHeight=150; CCSprite *sp=CCSprite::create("HelloWorld.png"); sp->setAnchorPoint(c ...
- JavaScript判断数据类型总结
最近做项目中遇到了一些关于javascript数据类型的判断处理,上网找了一下资料,并且亲自验证了各种数据类型的判断网页特效,在此做一个总结吧! 一.JS中的数据类型 1.数值型(Number):包 ...
- chmod -R o+rX /data
When using chmod -R o+rx /data , you set the execute permission on all directories as well as files ...