非常简单的实现,悍马车有两个型号,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所示。
图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) { //牛叉公司要H1型号的悍马 HummerModel h1 = new HummerH1Model(); //H1模型演示 h1.run(); } }
运行结果如下所示。
悍马H1发动...
悍马H1引擎声音是这样在...
悍马H1鸣笛...
悍马H1停车...
目前客户只要看H1型号的悍马车,没问题,生产出来,同时可以运行起来给他看看。非常非常的简单,那如果我告诉这就是模板方法模式你会不会很不屑呢?就这模式,太简单了,我一直在使用呀,是的,你经常在使用,但你不知道这是模板方法模式,那些所谓的高手就可以很牛X的说“用模板方法模式就可以实现…”,你还要很崇拜的看着,哇,牛人,模板方法模式是什么呀?这就是模板方法模式。\
扩展:
到目前为止,这两个模型都稳定的运行,突然有一天,老大急吼吼地找到了我:
“看你怎么设计的,车子一启动,喇叭就狂响,吵死人了!客户提出H1型号的悍马喇叭想让它响就响,H2型号的喇叭不要有声音,赶快修改一下”。
自己惹的祸,就要想办法解决它,稍稍思考一下,解决办法有了,先画出类图,如图10-4所示。
类图似乎改动很小,在抽象类HummerModel中增加了一个实现方法isAlarm,确定各个型号的悍马是否需要声音,由各个实现类覆写该方法,同时其他的基本方法由于不需要对外提供访问,因此也设计为protected类型。其源代码如代码清单10-8所示。
代码清单10-9 扩展后的抽象模板类
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(); //要让它叫的就是就叫,喇嘛不想让它响就不响 if(this.isAlarm()){ this.alarm(); } //到达目的地就停车 this.stop(); } //钩子方法,默认喇叭是会响的 protected boolean isAlarm(){ return true; } }
在抽象类中,isAlarm是一个实现方法,其作用是模板方法根据其返回值决定是否要响喇叭,子类可以覆写该返回值,由于H1型号的喇叭是想让它响就响,不想让它响就不响,由人控制了咯,其源代码如代码清单10-10所示。
代码清单10-10 扩展后的H1悍马
public class HummerH1Model extends HummerModel { private boolean alarmFlag = true; //要响喇叭 protected void alarm() { System.out.println("悍马H1鸣笛..."); } protected void engineBoom() { System.out.println("悍马H1引擎声音是这样在..."); } protected void start() { System.out.println("悍马H1发动..."); } protected void stop() { System.out.println("悍马H1停车..."); } protected boolean isAlarm() { return this.alarmFlag; } //要不要响喇叭,是有客户的来决定的 public void setAlarm(boolean isAlarm){ this.alarmFlag = isAlarm; } }
只要调用H1型号的悍马,默认是有喇叭响的,当然你可以不让喇叭响,就是通过isAlarm(false)就可以实现。H2型号的悍马是没有喇叭声响的,其源代码如代码清单10-11所示。
代码清单10-11 扩展后的H2悍马
public class HummerH2Model extends HummerModel { protected void alarm() { System.out.println("悍马H2鸣笛..."); } protected void engineBoom() { System.out.println("悍马H2引擎声音是这样在..."); } protected void start() { System.out.println("悍马H2发动..."); } protected void stop() { System.out.println("悍马H2停车..."); } //默认没有喇叭的 protected boolean isAlarm() { return false; } }
H2型号的悍马设置isAlarm()的返回值为false,也就是关闭了喇叭功能。场景类代码如代码清单10-12所示。
代码清单10-12 扩展后的场景类
public class Client { public static void main(String[] args) throws IOException { System.out.println("-------H1型号悍马--------"); System.out.println("H1型号的悍马是否需要喇叭声响?0-不需要 1-需要"); String type=(new BufferedReader(new InputStreamReader(System.in))).readLine(); HummerH1Model h1 = new HummerH1Model(); if(type.equals("0")){ h1.setAlarm(false); } h1.run(); System.out.println("\n-------H2型号悍马--------"); HummerH2Model h2 = new HummerH2Model(); h2.run(); } }
运行时需要交互的,首先,要求输入H1型号的悍马是否有声音,如下所示:
-------H1型号悍马--------
H1型号的悍马是否需要喇叭声响?0-不需要 1-需要
输入“0”后的运行结果如下所示:
-------H1型号悍马--------
H1型号的悍马是否需要喇叭声响?0-不需要 1-需要
0
悍马H1发动...
悍马H1引擎声音是这样在...
悍马H1停车...
-------H2型号悍马--------
悍马H2发动...
悍马H2引擎声音是这样在...
悍马H2停车...
输入“1”后的运行结果如下所示。
-------H1型号悍马--------
H1型号的悍马是否需要喇叭声响?0-不需要 1-需要
1
悍马H1发动...
悍马H1引擎声音是这样在...
悍马H1鸣笛...
悍马H1停车...
-------H2型号悍马--------
悍马H2发动...
悍马H2引擎声音是这样在...
悍马H2停车...
看到没,H1型号的悍马是由客户自己控制是否要响喇叭,也就是说外界条件改变,影响到模板方法的执行,在我们的抽象类中isAlarm的返回值就是影响了模板方法的执行结果,该方法就叫做钩子方法(Hook Method),有了钩子方法模板方法模式才算完美,大家可以想想,由子类的一个方法返回值决定决定公共部分的执行结果,是不是很有吸引力呀!
模板方法模式就是在模板方法中按照一定的规则和顺序调用基本方法,具体到我们上面那个例子就是run()方法按照规定的顺序(先调用start(),然后再调用engineBoom(),再调用alarm(),最后调用stop()调用本类的其他方法,并且由isAlarm()方法的返回值确定run()中的执行顺序变更。
模板方法模式的通用类图如图10-3所示。
原文:http://www.cnblogs.com/wsylly/p/3573563.html