模版方法模式
定义
定义一个操作中的算法的框架,而将算法中的一些步骤延迟到子类中。使得子类可以不改变这个算法框架,但却可以重定义算法的某些特定步骤。】
通用类图
模版方法模式非常简单,仅仅使用了Java的继承机制,其中的AbstractClass叫做抽象模版,它的方法分为两类:
基本方法:基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用。
模版方法:一般是一个具体方法,也就是一个骨架,实现对基本方法的调度,完成固定的逻辑。可以有一个或多个模版方法。
通用代码
抽象模版类:
public abstract class AbstractClass { //基本方法 protected abstract void doSomething(); //基本方法 protected abstract void doAnything(); //模板方法 public void templateMethod(){ /* * 调用基本方法,完成相关的逻辑 */ this.doAnything(); this.doSomething(); } }
具体实现类:
public class ConcreteClass1 extends AbstractClass { //实现基本方法 protected void doAnything() { //业务逻辑处理 } protected void doSomething() { //业务逻辑处理 } }
public class ConcreteClass2 extends AbstractClass { //实现基本方法 protected void doAnything() { //业务逻辑处理 } protected void doSomething() { //业务逻辑处理 } }
客户端使用:
public class Client { public static void main(String[] args) { AbstractClass class1 = new ConcreteClass1(); AbstractClass class2 = new ConcreteClass2(); //调用模板方法 class1.templateMethod(); class2.templateMethod(); } }
模版方法模式的优点
- 封装不变部分,扩展可变部分。
把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。我们悍马模型例子中,是不是就非常容易扩展,例如增加一个H3型号的悍马模型,很容易呀,增加一个子类,实现父类的基本方法就可以了。
- 提取公共部分代码,便于维护。
我们例子中刚刚走过的弯路就是最好的证明,如果我们不抽取到父类中,任由这种散乱的代码发生,想想后果是什么样子?维护人员为了修正一个缺陷,需要到处查找类似的代码!
- 行为控制交由子类来实现。
基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。
ps.重构里的提炼超类就是使用了模版方法模式。。。
含有钩子方法的模版方法模式
如下例:
模版抽象类:
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; } }
H1悍马具体实现类(通过setAlarm来控制它什么时候响喇叭):
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; } }
H2悍马具体实现类(设置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; } }
客户端使用:
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型号的悍马是由客户自己控制是否要响喇叭,也就是说外界条件改变,影响到模板方法的执行,在我们的抽象类中isAlarm的返回值就是影响了模板方法的执行结果,该方法就叫做钩子方法(Hook Method),有了钩子方法模板方法模式才算完美,大家可以想想,由子类的一个方法返回值决定决定公共部分的执行结果,是不是很有吸引力呀!
模板方法模式就是在模板方法中按照一定的规则和顺序调用基本方法,具体到我们上面那个例子就是run()方法按照规定的顺序(先调用start(),然后再调用engineBoom(),再调用alarm(),最后调用stop()调用本类的其他方法,并且由isAlarm()方法的返回值确定run()中的执行顺序变更。
ps.所有笔记摘自《设计模式之禅》。