设计模式之(七)-----模板方法模式

来源:互联网 发布:什么是美工设计助理 编辑:程序博客网 时间:2024/06/05 02:34

我先设计个类图:

 

非常简单的实现,你要悍马模型,我就给你悍马模型,先写个抽象类,然后两个不同型号的模型实现类,那我们把这个程序实现出来:

HummerModel抽象类的程序清单如下:

 

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 是悍马车辆模型的意思,不是悍马美女车模 
  4.  */  
  5. public abstract class HummerModel {  
  6.     ///首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了  
  7.     public abstract void start();  
  8.     //能发动,那还要能停下来,那才是真本事  
  9.     public abstract void stop();  
  10.     //喇叭会出声音,是滴滴叫,还是哔哔叫  
  11.     public abstract void alarm();  
  12.     //引擎会轰隆隆的响,不响那是假的  
  13.     public abstract void engineBoom();  
  14.     //那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑  
  15.     public abstract void run();  
  16. }  

H1型号悍马的定义如下:

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 悍马车是每个越野者的最爱,其中H1最接近军用系列 
  4.  */  
  5. public class HummerH1Model extends HummerModel {  
  6.     public void alarm() {  
  7.         System.out.println("悍马H1鸣笛...");  
  8.     }  
  9.     public void engineBoom() {  
  10.         System.out.println("悍马H1引擎声音是这样在...");  
  11.     }  
  12.     public void start() {  
  13.         System.out.println("悍马H1发动...");  
  14.     }  
  15.     public void stop() {  
  16.         System.out.println("悍马H1停车...");  
  17.     }  
  18.     //这个方法是很有意思的,它要跑,那肯定要启动,停止了等,也就是要调其他方法  
  19.     public void run() {  
  20.         //先发动汽车  
  21.         this.start();  
  22.         //引擎开始轰鸣  
  23.         this.engineBoom();  
  24.         //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭  
  25.         this.alarm();  
  26.         //到达目的地就停车  
  27.         this.stop();  
  28.     }  
  29. }  

 

然后看悍马H2型号的实现:

 

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * H1和H2有什么差别,还真不知道,真没接触过悍马 
  4.  */  
  5. public class HummerH2Model extends HummerModel {  
  6.     public void alarm() {  
  7.         System.out.println("悍马H2鸣笛...");  
  8.     }  
  9.     public void engineBoom() {  
  10.         System.out.println("悍马H2引擎声音是这样在...");  
  11.     }  
  12.     public void start() {  
  13.         System.out.println("悍马H2发动...");  
  14.     }  
  15.     public void stop() {  
  16.         System.out.println("悍马H2停车...");  
  17.     }  
  18.     //H2要跑,那肯定要启动,停止了等,也就是要调其他方法  
  19.     public void run() {  
  20.         //先发动汽车  
  21.         this.start();  
  22.         //引擎开始轰鸣  
  23.         this.engineBoom();  
  24.         //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭  
  25.         this.alarm();  
  26.         //到达目的地就停车  
  27.         this.stop();  
  28.     }  
  29. }  

然后程序写到这里,你就看到问题了,run方法的实现应该在抽象类上,不应该在实现类上,好,我们修改一下类图和实现:

就把run方法放到了抽象类中,那代码也相应的改变一下,先看HummerModel.java:

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 是悍马车辆模型的意思,不是悍马美女车模 
  4.  */  
  5. public abstract class HummerModel {  
  6.     ///首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了  
  7.     public abstract void start();  
  8.     //能发动,那还要能停下来,那才是真本事  
  9.     public abstract void stop();  
  10.     //喇叭会出声音,是滴滴叫,还是哔哔叫  
  11.     public abstract void alarm();  
  12.     //引擎会轰隆隆的响,不响那是假的  
  13.     public abstract void engineBoom();  
  14.     //那模型应该会跑吧,别管是人退的,还是电力驱动,总之要会跑  
  15.     public void run() {  
  16.         //先发动汽车  
  17.         this.start();  
  18.         //引擎开始轰鸣  
  19.         this.engineBoom();  
  20.         //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭  
  21.         this.alarm();  
  22.         //到达目的地就停车  
  23.         this.stop();  
  24.     }  
  25. }  

下面是HummerH1Model.java程序清单:

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 悍马车是每个越野者的最爱,其中H1最接近军用系列 
  4.  */  
  5. public class HummerH1Model extends HummerModel {  
  6.     public void alarm() {  
  7.         System.out.println("悍马H1鸣笛...");  
  8.     }  
  9.     public void engineBoom() {  
  10.         System.out.println("悍马H1引擎声音是这样在...");  
  11.     }  
  12.     public void start() {  
  13.         System.out.println("悍马H1发动...");  
  14.     }  
  15.     public void stop() {  
  16.         System.out.println("悍马H1停车...");  
  17.     }  
  18. }  

下面是HummerH2Model.java的程序清单:

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * H1和H2有什么差别,还真不知道,真没接触过悍马 
  4.  */  
  5. public class HummerH2Model extends HummerModel {  
  6.     public void alarm() {  
  7.         System.out.println("悍马H2鸣笛...");  
  8.     }  
  9.     public void engineBoom() {  
  10.         System.out.println("悍马H2引擎声音是这样在...");  
  11.     }  
  12.     public void start() {  
  13.         System.out.println("悍马H2发动...");  
  14.     }  
  15.     public void stop() {  
  16.         System.out.println("悍马H2停车...");  
  17.     }  
  18. }  

类图修改完毕了,程序也该好了,提交给老大,老大一看,挺好,就开始生产了,并提交给客户使用了,那客户是如何使用的呢?类图上增加一个Client类,就是客户,我们这个是用main函数来代替他使用,类图如下:

然后看增加的Client.java程序,非常的简单:

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 客户开始使用这个模型 
  4.  */  
  5. public class Client {  
  6.     public static void main(String[] args) {  
  7.         //客户开着H1型号,出去遛弯了  
  8.         HummerModel h1 = new HummerH1Model();  
  9.         h1.run(); //汽车跑起来了;  
  10.         //客户开H2型号,出去玩耍了  
  11.         HummerModel h2 = new HummerH2Model();  
  12.         h2.run();  
  13.     }  
  14. }  

非常非常的简单,那如果我告诉这就是模板方法模式你会不会很不屑呢?就这模式,太简单了,我一直在使用呀,是的,你经常在使用,但你不知道这是模板方法模式,那些所谓的高手就可以很牛X的说“用模板方法模式就可以实现…”,你还要很崇拜的看着,哇,牛人,模板方法模式是什么呀?

然后我们继续回顾我们这个模型,回头一想,不对呀,需求分析的有点问题,客户要关心模型的启动,停止,鸣笛,引擎声音吗?他只要在run的过程中,听到或看都成了呀,暴露那么多的方法干啥?好了,我们重新修改一下类图:

 

把抽象类上的四个方法设置为protected访问权限,好了,既然客户不关心这几个方法,而且这四个方法都是由子类来实现的,那就设置成protected模式。咦~,那还有个缺陷,run方法既然子类都不修改,那是不是可以设置成final类型呢?是滴是滴,类图如下:

好了,这才是模板方法模式,就是这个样子,我们只要修改抽象类代码就可以了,HummerModel.java程序清单如下:

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 是悍马车辆模型的意思,不是悍马美女车模 
  4.  */  
  5. public abstract class HummerModel {  
  6.     ///首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了  
  7.     protected abstract void start();  
  8.     //能发动,那还要能停下来,那才是真本事  
  9.     protected abstract void stop();  
  10.     //喇叭会出声音,是滴滴叫,还是哔哔叫  
  11.     protected abstract void alarm();  
  12.     //引擎会轰隆隆的响,不响那是假的  
  13.     protected abstract void engineBoom();  
  14.     //那模型应该会跑吧,别管是人退的,还是电力驱动,总之要会跑  
  15.     final public void run() {  
  16.         //先发动汽车  
  17.         this.start();  
  18.         //引擎开始轰鸣  
  19.         this.engineBoom();  
  20.         //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭  
  21.         this.alarm();  
  22.         //到达目的地就停车  
  23.         this.stop();  
  24.     }  
  25. }  

其他的子类都不用修改(如果要修改,就是把四个方法的访问权限由public修改protected),大家请看这个run方法,他定义了调用其他方法的顺序,并且子类是不能修改的,这个叫做模板方法;start、stop、alarm、engineBoom这四个方法是子类必须实现的,而且这四个方法的修改对应了不同的类,这个叫做基本方法,基本方法又分为三种:在抽象类中实现了的基本方法叫做具体方法;在抽象类中没有实现,在子类中实现了叫做抽象方法,我们这四个基本方法都是抽象方法,由子类来实现的;还有一种叫做钩子方法,这个等会讲。到目前为止,这两个模型都稳定的运行,突然有一天,老大又找到了我,“客户提出新要求了,那个喇叭想让它响就响,你看你设计的模型,车子一启动,喇叭就狂响,赶快修改一下”,确实是设计缺陷,呵呵,不过是我故意的,那我们怎么修改呢?看修改后的类图:

 

 增加一个方法,isAlarm(),喇嘛要不要响,这就是钩子方法(Hook Method),那我们只要修改一下抽象类就可以了:

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 是悍马车辆模型的意思,不是悍马美女车模 
  4.  */  
  5. public abstract class HummerModel {  
  6.     ///首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了  
  7.     protected abstract void start();  
  8.     //能发动,那还要能停下来,那才是真本事  
  9.     protected abstract void stop();  
  10.     //喇叭会出声音,是滴滴叫,还是哔哔叫  
  11.     protected abstract void alarm();  
  12.     //引擎会轰隆隆的响,不响那是假的  
  13.     protected abstract void engineBoom();  
  14.     //那模型应该会跑吧,别管是人退的,还是电力驱动,总之要会跑  
  15.     final public void run() {  
  16.         //先发动汽车  
  17.         this.start();  
  18.         //引擎开始轰鸣  
  19.         this.engineBoom();  
  20.         //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭  
  21.         if(this.isAlarm()){  
  22.             this.alarm();  
  23.         }  
  24.         //到达目的地就停车  
  25.         this.stop();  
  26.     }  
  27.     //钩子方法,默认喇叭是会响的  
  28.     protected boolean isAlarm(){  
  29.         return true;  
  30.     }  
  31. }  

钩子方法模式是由抽象类来实现的,子类可以重写的,H2型号的悍马是不会叫的,喇叭是个摆设,看HummerH2Model.java代码:

 

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * H1和H2有什么差别,还真不知道,真没接触过悍马 
  4.  */  
  5. public class HummerH2Model extends HummerModel {  
  6.     public void alarm() {  
  7.         System.out.println("悍马H2鸣笛...");  
  8.     }  
  9.     public void engineBoom() {  
  10.         System.out.println("悍马H2引擎声音是这样在...");  
  11.     }  
  12.     public void start() {  
  13.         System.out.println("悍马H2发动...");  
  14.     }  
  15.     public void stop() {  
  16.         System.out.println("悍马H2停车...");  
  17.     }  
  18.     //默认没有喇叭的  
  19.     protected boolean isAlarm() {  
  20.         return false;  
  21.     }  
  22. }  

 

那H2型号的模型都没有喇叭,就是按了喇叭也没有声音,那客户端这边的调用没有任何修改,出来的结果就不同,我们先看Client.java程序:

 

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 客户开始使用这个模型 
  4.  */  
  5. public class Client {  
  6.     public static void main(String[] args) {  
  7.         HummerH2Model h2 = new HummerH2Model();  
  8.         h2.run(); //H2型号的悍马跑起来  
  9.     }  
  10. }  

那H1又有所不同了,它的喇叭要不要响是由客户来决定,其实在类图上已经标明了setAlarm这个方法,我们看HummerH1Model.java的代码:

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 悍马车是每个越野者的最爱,其中H1最接近军用系列 
  4.  */  
  5. public class HummerH1Model extends HummerModel {  
  6.     private boolean alarmFlag = true//是否要响喇叭  
  7.     public void alarm() {  
  8.         System.out.println("悍马H1鸣笛...");  
  9.     }  
  10.     public void engineBoom() {  
  11.         System.out.println("悍马H1引擎声音是这样在...");  
  12.     }  
  13.     public void start() {  
  14.         System.out.println("悍马H1发动...");  
  15.     }  
  16.     public void stop() {  
  17.         System.out.println("悍马H1停车...");  
  18.     }  
  19.     protected boolean isAlarm() {  
  20.         return this.alarmFlag;  
  21.     }  
  22.     //要不要响喇叭,是有客户的来决定的  
  23.     public void setAlarm(boolean isAlarm){  
  24.         this.alarmFlag = isAlarm;  
  25.     }  
  26. }  

 

这段代码呢修改了两个地方,一是重写了父类的isAlarm()方法,一是增加了一个setAlarm方法,由调用者去决定是否要这个功能,也就是喇叭要不要滴滴答答的响,哈哈,那我们看看Client.java的修改:

 

[java] view plaincopyprint?
  1. package template.method.pattern;  
  2. /** 
  3.  * 客户开始使用这个模型 
  4.  */  
  5. public class Client {  
  6.     public static void main(String[] args) {  
  7.         //客户开着H1型号,出去遛弯了  
  8.         HummerH1Model h1 = new HummerH1Model();  
  9.         h1.setAlarm(true);  
  10.         h1.run(); //汽车跑起来了;  
  11.     }  
  12. }  

看到没,这个模型run起来就有声音了,那当然把h1.setAlarm(false)运行起来喇叭就没有声音了,钩子方法的作用就是这样滴。

0 0