设计模式学习(九)——模板方法模式
来源:互联网 发布:java经典程序 源代码 编辑:程序博客网 时间:2024/05/18 02:12
参考书——《HeadFirst设计模式》
和书中一样,用冲咖啡和沏茶来说这个设计模式
冲咖啡: 沏茶: 制作饮料:
step1 烧水 烧水 烧水
step2 用沸水冲咖啡粉 用沸水冲茶叶 ===========》 用沸水冲
step3 倒入杯中 倒入杯中 倒入杯中
step4 加糖加奶 加柠檬 加适当的调料
我们看到step1和step3是一样的,换而言之,可以具体实现,step2和step4虽然不一样但是我们可以进行提取,也就是抽象。等等,想到了什么?!抽象类 P.s:抽象类和接口的区别http://dev.yesky.com/436/7581936.shtml
public abstract class Beverage {final void prepareRecipe() {boilwater();brew();pourIncup();addCondiments();}abstract void brew();abstract void addCondiments();void boilwater() {System.out.println("烧水");} void pourIncup() { System.out.println("倒入杯中");}}
模板方法——就是这里的prepareRecipe(),它定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
“炫酷的地方”:1)Beverage类主导一切,它拥有这个算法,并且保护这个算法;
2)对子类来说,Beverage类的存在,可以提高代码的复用;
3)算法存在一个地方,便于维护;
4)模板方法提供了一个框架,可以让其他的饮料插进来,新的饮料只要实现自己的方法就行;
5)Beverage类专注在算法本身,而由子类提供完整的实现。
定义:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
其实,模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中任何步骤都可以是抽象的,由子类负责实现,这可以确保算法的结构保持不变,同时由子类提供部分实现。模板方法本身和这些抽象的原语操作的具体实现之间解耦了。
有了这个抽象类,我们就可以写具体的Coffee和Tea类了,代码如下:
public class Coffee extends Beverage {void brew() {System.out.println("用沸水冲咖啡"); } void addCondiments() {System.out.println("加糖加奶"); }}
到这里,发现问题了,冲杯Coffee,前三个步骤是必须的,但是添加调料就不是必须的了,有的人不喜欢给咖啡里加糖加奶,怎么办?
基于这个问题我们引出"钩子"方法:
在上面的链接的这篇文章里,有一段话“在abstract class的定义中,我们可以赋予方法的默认行为。”
对此我的理解:给一个方法赋予默认行为,这个方法是子类可以重写也可以不重写,子类可以通过重写来提升方法的性能,这样的方法就可以成为“钩子”方法。有人会说,普通的方法(没有被final修饰)也可以做到可以重写也可以不重写啊。难道都是“钩子”方法?!
在别人的博客看到这样的话:“继承包含这样一层含义:父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。”
所以,我觉得“钩子”方法和普通方法,其实是个度的问题,普通方法,就如上面这段话的红色字所说,它有它自己规定的契约,不太希望被改变。而“钩子”方法是规定了默认行为(这个默认行为甚至可以为空,“钩子”方法什么都不做),最为宽泛的,子类可以在其上进行规定。
接下来,我们看看如何用"钩子"方法解决上面的问题——有的人不喜欢给咖啡里加糖加奶,怎么办?
先来看Beverage类:
public abstract class Beverage {final void prepareRecipe() {boilwater();brew();pourIncup();if(customerWantsCondiments()){addCondiments();}}abstract void brew();abstract void addCondiments();void boilwater() {System.out.println("烧水");} void pourIncup() { System.out.println("倒入杯中");} boolean customerWantsCondiments() { return true; }}
这个customerWantsCondiments()就是一个“钩子”方法,只会返回true,不做别的事,子类可以覆盖也可以不覆盖。
再来看Coffee类:
public class Coffee extends Beverage {void brew() {System.out.println("用沸水冲咖啡"); } void addCondiments() {System.out.println("加糖加奶"); }boolean customerWantsCondiments() { String answer = getUserInput(); if(answer.toLowerCase().startsWith("y")){ return true; }else{ return false; } }public String getUserInput() {String answer = null;System.out.println("需要加糖加奶么?(y/n)");BufferedReader in = new BufferedReader(new InputStreamReader(System.in));try{answer = in.readLine();}catch(IOException e){System.out.println("抱歉,出错了!");}if(answer==null){answer = "no"; }return answer;}}
问题解决了!“钩子”方法竟然能够作为条件控制,影响抽象类中的算法流程,实在不赖吧
这里我们总结下“钩子”方法的几个作用:
1)可以让子类实现算法中可选的部分;
2)在“钩子”方法对子类的实现并不重要的时候,子类可以对它置之不理;
3)让子类能够有机会对模板方法中即将发生的(or刚刚发生的)步骤做出反应;
4)也可以让子类有能力为抽象类做一些决定(上面的代码用的就是这个)。
P.S:
好莱坞原则:别调用我们,我们会调用你。
在好莱坞原则下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。也就是说高层组件对低层组件采用了好莱坞原则。
好莱坞原则和模板方法模式
还是用我们上述的例子来说。
Beverage是我们的高层组件,它能够控制饮料的冲泡算法,只有在需要子类实现某个方法时,才调用子类。
饮料的客户代码只依赖Beverage抽象,而不依赖具体的Tea或Coffee,这可以减少整个系统的依赖。
- 设计模式学习(九)——模板方法模式
- 设计模式学习(九)————模板方法模式
- iOS设计模式(九) 模板方法模式
- 设计模式(九)模板方法模式
- 设计模式(九)--模板方法模式
- 设计模式学习笔记——模板方法模式
- 学习设计模式之禅——模板方法模式
- 设计模式学习笔记——模板方法模式
- 设计模式学习——模板方法模式
- 设计模式学习笔记——模板方法模式
- 七、模板方法模式——设计模式学习笔记
- 设计模式学习笔记—模板方法模式
- Head First设计模式学习—模板方法、策略模式
- 设计模式——模板方法模式
- 设计模式——模板方法模式
- 设计模式——模板方法模式
- 设计模式——模板方法模式
- 设计模式——模板方法模式
- Android之PDF文件的读取——网络资源vudroid异常解决贴
- 用JAVA编写一个简单密码框
- 让光标在文本框的第一个位置出现
- Python 文件与目录操作 .
- Linux的常用基本命令2
- 设计模式学习(九)——模板方法模式
- 指针函数与函数指针的区别
- 白话机器学习算法(十一) EM(附三个硬币模型代码)
- 微软发布iPad版Office:编辑文件需付费
- IOS ipad拍照问题
- HTML5去除input [type=search] 的默认边框和删除按钮
- 做了一些無聊的事
- 【李明杰老师分享】iOS图片拉伸技巧
- 相关系数