24种设计模式--模版方法模式【Template Method Pattern】

  周三,9:00,我刚刚坐到位置,打开电脑准备开始干活。“小三,小三,叫一下其它同事,到会议室,开会”老大跑过来吼,带着淫笑。还不等大家坐稳,老大就开讲了,“告诉大家一个好消息,昨天终于把牛叉模型公司的口子打开了,要我们做悍马模型,虽然是第一个车辆模型,但是我们有能力,有信心做好,我们一定要…(中间省略 20 分钟的讲话,如果你听过领导人的讲话,这个你应该能够续上)”动员工作做完了,那就开始压任务了,“这次时间是非常紧张的,只有一个星期的时间,小三,你负责在一个星期的时间把这批 10 万车模(注:车模是车辆模型的意思,不是香车美女那个车模)建设完成…”“一个星期?这个…,是真做不完,要做分析,做模板,做测试,还要考虑扩展性、稳定性、健壮性等,时间实在是太少了”还没等老大说完,我就急了,再不急我的小命就折在上面了!“那这样,你只做实现,不考虑使用设计模式,扩展性等都不用考虑”老大又把我压回去了。“不考虑设计模式?那…”哎,领导已经布置任务了,那就开始死命的做吧,命苦不能怨政府,点背不能怪社会呀,然后就开始准备动手做,在做之前先介绍一下我们公司的背景,我们公是做模型生产的,做过桥梁模型、建筑模型、机械模型,甚至是一些政府、军事的机密模型,这个不能说,就是把真实的实物按照一定的比例缩小或放大,用于试验、分析、量化或者是销售等等,上面提到的牛叉模型公司专门销售车辆模型的公司,自己不生产,我们公司是第一次从牛叉模型公司接单,那我怎么着也要把活干好,可时间很紧张呀,怎么办?既然领导都说了,不考虑扩展性,那好办,我先设计个类图:

  1 package com.pattern.templateMethod;
  2
  3 /**
  4  * 悍马车辆模型
  5  * @author http://www.cnblogs.com/initial-road/
  6  *
  7  */
  8 public abstract class HummerModel {
  9
 10     /**
 11      * 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了
 12      */
 13     protected abstract void start();
 14
 15     /**
 16      * 能发动,那还要能停下来,那才是真本事
 17      */
 18     protected abstract void stop();
 19
 20     /**
 21      * 喇叭会出声音,是滴滴叫,还是哗哗叫
 22      */
 23     protected abstract void alarm();
 24
 25     /**
 26      * 引擎会轰隆隆的响,不响那是假的
 27      */
 28     protected abstract void engineBoom();
 29
 30     /**
 31      * 那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
 32      */
 33     public abstract void run();
 34
 35 }
 36
 37 H1 型号悍马的定义如下:
 38 package com.pattern.templateMethod;
 39
 40 /**
 41  * 悍马车是每个越野者的最爱,其中H1最接近军用系列
 42  * @author http://www.cnblogs.com/initial-road/
 43  *
 44  */
 45 public class HummerH1Model extends HummerModel {
 46
 47     @Override
 48     public void alarm() {
 49         System.out.println("悍马H1鸣笛...");
 50     }
 51
 52     @Override
 53     public void engineBoom() {
 54         System.out.println("悍马H1引擎声音...");
 55     }
 56
 57     @Override
 58     public void start() {
 59         System.out.println("悍马H1发动...");
 60     }
 61
 62     @Override
 63     public void stop() {
 64         System.out.println("悍马H1停车...");
 65     }
 66
 67         /**
 68      * 这个方法是很有意思的,它要跑,那肯定是启动,停止了等,也就是有调用其他方法
 69      */
 70     @Override
 71     public void run(){
 72
 73         //先发动汽车
 74         this.start();
 75
 76         //引擎开始轰鸣
 77         this.engineBoom();
 78
 79         //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
 80         this.alarm();
 81
 82         //达到目的地就停车
 83         this.stop();
 84
 85     }
 86
 87 }
 88
 89 然后看悍马 H2 型号的实现:
 90 package com.pattern.templateMethod;
 91
 92 /**
 93  * H1和H2有什么差别,还真不知道,真没接触过悍马
 94  * @author http://www.cnblogs.com/initial-road/
 95  *
 96  */
 97 public class HummerH2Model extends HummerModel {
 98
 99     @Override
100     public void alarm() {
101         System.out.println("悍马H2鸣笛...");
102     }
103
104     @Override
105     public void engineBoom() {
106         System.out.println("悍马H2引擎声音是这样的...");
107     }
108
109     @Override
110     public void start() {
111         System.out.println("悍马H2发动...");
112     }
113
114     @Override
115     public void stop() {
116         System.out.println("悍马H2停车...");
117     }
118
119         /**
120      * H2要跑,那肯定要启动,停止了等,也就是要调用其他方法
121      */
122     @Override
123     public void run(){
124
125         //先发动汽车
126         this.start();
127
128         //引擎开始轰鸣
129         this.engineBoom();
130
131         //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
132         this.alarm();
133
134         //达到目的地就停车
135         this.stop();
136
137     }
138
139 }

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

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

  1 package com.pattern.templateMethod;
  2
  3 /**
  4  * 悍马车辆模型
  5  * @author http://www.cnblogs.com/initial-road/
  6  *
  7  */
  8 public abstract class HummerModel {
  9
 10     /**
 11      * 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了
 12      */
 13     protected abstract void start();
 14
 15     /**
 16      * 能发动,那还要能停下来,那才是真本事
 17      */
 18     protected abstract void stop();
 19
 20     /**
 21      * 喇叭会出声音,是滴滴叫,还是哗哗叫
 22      */
 23     protected abstract void alarm();
 24
 25     /**
 26      * 引擎会轰隆隆的响,不响那是假的
 27      */
 28     protected abstract void engineBoom();
 29
 30     /**
 31      * 那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
 32      */
 33     public void run(){
 34
 35             //先发动汽车
 36             this.start();
 37
 38             //引擎开始轰鸣
 39             this.engineBoom();
 40
 41             //然后开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
 42             this.alarm();
 43
 44             //达到目的地就停车
 45             this.stop();
 46
 47     }
 48
 49 }
 50
 51 下面是 HummerH1Model.java 程序清单:
 52 package com.pattern.templateMethod;
 53
 54 /**
 55  * 悍马车是每个越野者的最爱,其中H1最接近军用系列
 56  * @author http://www.cnblogs.com/initial-road/
 57  *
 58  */
 59 public class HummerH1Model extends HummerModel {
 60
 61     @Override
 62     public void alarm() {
 63         System.out.println("悍马H1鸣笛...");
 64     }
 65
 66     @Override
 67     public void engineBoom() {
 68         if(this.isAlarm())
 69             System.out.println("悍马H1引擎声音...");
 70     }
 71
 72     @Override
 73     public void start() {
 74         System.out.println("悍马H1发动...");
 75     }
 76
 77     @Override
 78     public void stop() {
 79         System.out.println("悍马H1停车...");
 80     }
 81
 82 }
 83
 84 下面是 HummerH2Model.java 的程序清单:
 85 package com.pattern.templateMethod;
 86
 87 /**
 88  * H1和H2有什么差别,还真不知道,真没接触过悍马
 89  * @author http://www.cnblogs.com/initial-road/
 90  *
 91  */
 92 public class HummerH2Model extends HummerModel {
 93
 94     @Override
 95     public void alarm() {
 96         if(this.isAlarm())
 97             System.out.println("悍马H2鸣笛...");
 98     }
 99
100     @Override
101     public void engineBoom() {
102         System.out.println("悍马H2引擎声音是这样的...");
103     }
104
105     @Override
106     public void start() {
107         System.out.println("悍马H2发动...");
108     }
109
110     @Override
111     public void stop() {
112         System.out.println("悍马H2停车...");
113     }
114
115 }

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

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

 1 package com.pattern.templateMethod;
 2
 3 /**
 4  * 客户开始使用这个模型
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public class Client {
 9
10     public static void main(String[] args) {
11
12         //客户开着H1型号,出去遛弯了
13         HummerH1Model h1 = new HummerH1Model();
14         //驱车跑起来了
15         h1.run();
16
17         //客户开着H2型号,出去玩耍了
18         HummerModel h2 = new HummerH2Model();
19         h2.run();
20     }
21
22 }

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

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

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

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

 1 package com.pattern.templateMethod;
 2
 3 /**
 4  * 悍马车辆模型
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public abstract class HummerModel {
 9
10     /**
11      * 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了
12      */
13     protected abstract void start();
14
15     /**
16      * 能发动,那还要能停下来,那才是真本事
17      */
18     protected abstract void stop();
19
20     /**
21      * 喇叭会出声音,是滴滴叫,还是哗哗叫
22      */
23     protected abstract void alarm();
24
25     /**
26      * 引擎会轰隆隆的响,不响那是假的
27      */
28     protected abstract void engineBoom();
29
30     /**
31      * 那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
32      */
33     final public void run(){
34
35             //先发动汽车
36             this.start();
37
38             //引擎开始轰鸣
39             this.engineBoom();
40
41             //然后开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
42             this.alarm();
43
44             //达到目的地就停车
45             this.stop();
46
47     }
48
49 }

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

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

 1 package com.pattern.templateMethod;
 2
 3 /**
 4  * 悍马车辆模型
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public abstract class HummerModel {
 9
10     /**
11      * 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正是要能够发动起来,那这个实现要在实现类里了
12      */
13     protected abstract void start();
14
15     /**
16      * 能发动,那还要能停下来,那才是真本事
17      */
18     protected abstract void stop();
19
20     /**
21      * 喇叭会出声音,是滴滴叫,还是哗哗叫
22      */
23     protected abstract void alarm();
24
25     /**
26      * 引擎会轰隆隆的响,不响那是假的
27      */
28     protected abstract void engineBoom();
29
30     /**
31      * 那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
32      */
33     final public void run(){
34
35             //先发动汽车
36             this.start();
37
38             //引擎开始轰鸣
39             this.engineBoom();
40
41             //然后开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
42             this.alarm();
43
44             //达到目的地就停车
45             this.stop();
46
47     }
48
49     /**
50      * 钩子方法,默认喇叭是会响的
51      */
52     protected boolean isAlarm(){
53         return true;
54     }
55
56 }

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

 1 package com.pattern.templateMethod;
 2
 3 /**
 4  * H1和H2有什么差别,还真不知道,真没接触过悍马
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public class HummerH2Model extends HummerModel {
 9
10     @Override
11     public void alarm() {
12         if(this.isAlarm())
13             System.out.println("悍马H2鸣笛...");
14     }
15
16     @Override
17     public void engineBoom() {
18         System.out.println("悍马H2引擎声音是这样的...");
19     }
20
21     @Override
22     public void start() {
23         System.out.println("悍马H2发动...");
24     }
25
26     @Override
27     public void stop() {
28         System.out.println("悍马H2停车...");
29     }
30
31     //默认没有喇叭的
32     @Override
33     protected boolean isAlarm(){
34         return false;
35     }
36
37 }

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

 1 package com.pattern.templateMethod;
 2
 3 /**
 4  * 客户开始使用这个模型
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public class Client {
 9
10     public static void main(String[] args) {
11
12         HummerModel h2 = new HummerH2Model();
13                 //客户开着H2型号跑起来了
14         h2.run();
15     }
16
17 }

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

 1 package com.pattern.templateMethod;
 2
 3 /**
 4  * 悍马车是每个越野者的最爱,其中H1最接近军用系列
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public class HummerH1Model extends HummerModel {
 9     protected boolean alarmFlag = true;    //是否要响喇叭
10
11     @Override
12     public void alarm() {
13         System.out.println("悍马H1鸣笛...");
14     }
15
16     @Override
17     public void engineBoom() {
18         if(this.isAlarm())
19             System.out.println("悍马H1引擎声音...");
20     }
21
22     @Override
23     public void start() {
24         System.out.println("悍马H1发动...");
25     }
26
27     @Override
28     public void stop() {
29         System.out.println("悍马H1停车...");
30     }
31
32     public void setAlarmFlag(boolean alarmFlag) {
33         this.alarmFlag = alarmFlag;
34     }
35
36     @Override
37     protected boolean isAlarm() {
38         return this.alarmFlag;
39     }
40
41 }

  这段代码呢修改了两个地方,一是重写了父类的 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 方法,就完成你的扩展开发,确实是一种简单的模式。

时间: 2024-12-24 04:45:30

24种设计模式--模版方法模式【Template Method Pattern】的相关文章

设计模式 笔记 模版方法模式 Template Method

//---------------------------15/04/28---------------------------- //TemplateMethod 模版方法模式----类行为型模式 /* 1:意图: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.TemplateMethod使得子类可以不改变 一个算法的结构即可重定义该算法的某些特定步骤. 2:动机: 3:适用性: 1>一次性实现算法的不变的部分,并将可变的行为留给子类来实现. 2>各子类中的公共行为应被提取出来并集中

24种设计模式--中介者模式【Mediator Pattern】

各位好,大家都是来自五湖四海,都要生存,于是都找了个靠山——公司,给你发薪水的地方,那公司就要想尽办法盈利赚钱,盈利方法则不尽相同,但是作为公司都有相同三个环节:采购.销售和库存,这个怎么说呢?比如一个软件公司,要开发软件,需要开发环境吧, Windows 操作系统,数据库产品等,这你得买吧,那就是采购,开发完毕一个产品还要把产品推销出去,推销出去了大家才有钱赚,不推销出去大家都去喝西北风呀,既然有产品就必然有库存,软件产品也有库存,你总要拷贝吧,虽然是不需要占用库房空间,那也是要占用光盘或硬盘

.NET设计模式(16):模版方法(Template Method)(转)

摘要:Template Method模式是比较简单的设计模式之一,但它却是代码复用的一项基本的技术,在类库中尤其重要. 主要内容 1.概述 2.Template Method解说 3..NET中的Template Method模式 4.适用性及实现要点 概述 变化一直以来都是软件设计的永恒话题,在XP编程中提倡拥抱变化,积极应对.如何更好的去抓住变化点,应对变化?如何更好的提高代码复用?通过学习Template Method模式,您应该有一个新的认识. 意图 定义一个操作中的算法的骨架,而将一些

设计模式之模板方法模式(Template Method)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

php设计模式——工厂方法模式(Factory Method)

二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 1 <?php 2 /* 3 * php设计模式——工厂方法模式(Factory Method) 4 */ 5 6 7 /* 8 * IAp

Android设计模式——工厂方法模式(Factory Method)

二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 1 package com.example.main; 2 3 import android.app.Activity; 4 import

设计模式 -- 模版方法模式

模板方法模式:定义一个算法的执行骨架,将具体的算法实现延迟到子类完成. 模板方法模式需要开发抽象类和具体子类的设计师之间的协作.一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤.代表这些具体逻辑步骤的方法称做基本方法(primitive method):而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来. 举个例子: 在现实生活中,很多事情都包含几个实现步骤,例如请客吃饭,无论吃什么,一般都包含点单.吃

设计模式 - 模板方法模式(template method pattern) 详解

模板方法模式(template method pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy 模板方法模式(template method pattern): 在一个方法中定义一个算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以在不改变算法结构的情况下, 重新定义算法中的某些步骤. 模板方法可以进行挂钩(hook), 钩子(hook)是一种被声明在抽象类中的方法, 但只有空的或者默认的实现. 钩子的存在, 可以让子类有能力

设计模式 模版方法模式 展现程序员的一天

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/26276093 继续设计模式~ 模版方法模式 老套路,先看下定义:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤. 简单看下定义,模版方法定义了一个算法的步骤,并且允许子类为一个或多个步骤提供实现.定义还算清晰,下面来个例子展示下本公司的上班情况(纯属娱乐,如有雷同,请对号入座).简单描述一下:本公司有程序猿.测试