模板方法模式
业务流程固定,只是具体某些步骤存在可变化的部分。如银行办理业务流程
银行办理业务流程:
Step 1、进门取号
Step 2、填写单据
Step 3、等待叫号
Step 4、窗口办理
其中Step 2业务随着办理的业务不同,填写的单据不同,但是其他3个步骤都是相同的。
例子:饮料机可以冲泡咖啡和茶叶
提神饮料泡法:
1)把水煮沸(boilWater)
2)泡饮料(brew)
3)把饮料倒进杯子(pourInCup)
4)加入调味料(addCondiments)
冲咖啡和冲茶叶的流程是相同的,只是步骤2和步骤4分别不同,所以可以使用模板方法模式进行设计和实现。
1、抽象基类定义框架
package com.templatemehod; //模版方法模式 //抽象基类,为所有子类提供一个算法框架 //提神饮料 public abstract class RefreshBeverage { //制备饮料的模版方法 //封装了所有子类共同遵循的算法框架 public final void prepareBeverageTemplate(){ //步骤1 将水煮沸 boilWater(); //步骤2 泡制饮料 brew(); //步骤3 将饮料倒入杯中 pourInCup(); //步骤4 加入调味料 addCondiments(); } private void boilWater() { System.out.println("1、将水煮沸"); } private void pourInCup() { System.out.println("3、将饮料倒入杯中"); } protected abstract void brew(); protected abstract void addCondiments(); }
2、具体子类实现延迟步骤
package com.templatemehod; //具体子类,提供了咖啡制备的具体实现 public class Coffee extends RefreshBeverage { @Override protected void brew() { System.out.println("2、用沸水冲泡咖啡"); } @Override protected void addCondiments() { System.out.println("4、加入糖和牛奶"); } }
package com.templatemehod; //具体子类,提供了制备茶的具体实现 public class Tea extends RefreshBeverage { @Override protected void brew() { System.out.println("2、用80度热水浸泡茶叶5分钟"); } @Override protected void addCondiments() { System.out.println("4、加入柠檬"); } }
3、测试一下
package com.templatemehod; public class RefreshBeverageTest { public static void main(String[] args) { System.out.println("制备咖啡..."); RefreshBeverage b1 = new Coffee(); b1.prepareBeverageTemplate(); System.out.println("咖啡好了..."); System.out.println(" ..........................."); System.out.println("制备茶..."); RefreshBeverage b2 = new Tea(); b2.prepareBeverageTemplate(); System.out.println("茶好了..."); } }
测试结果:
制备咖啡...
1、将水煮沸
2、用沸水冲泡咖啡
3、将饮料倒入杯中
4、加入糖和牛奶
咖啡好了...
...........................
制备茶...
1、将水煮沸
2、用80度热水浸泡茶叶5分钟
3、将饮料倒入杯中
4、加入柠檬
茶好了...
4、钩子使子类更灵活
如:冲茶叶的时候不想加入调料,冲咖啡的时候还是要加入糖和牛奶
1、抽象基类定义框架
package com.templatemehod; //模版方法模式 //抽象基类,为所有子类提供一个算法框架 //提神饮料 public abstract class RefreshBeverage { //制备饮料的模版方法 //封装了所有子类共同遵循的算法框架 public final void prepareBeverageTemplate(){ //步骤1 将水煮沸 boilWater(); //步骤2 泡制饮料 brew(); //步骤3 将饮料倒入杯中 pourInCup(); //步骤4 加入调味料 if(isCustomerWantsCondiments()){ addCondiments(); } } //Hook,钩子函数,提供一个默认或空的实现 //具体的子类可以自行决定是否挂钩以及如何挂钩 //询问用户是否加入调料 protected boolean isCustomerWantsCondiments() { return true; } private void boilWater() { System.out.println("1、将水煮沸"); } private void pourInCup() { System.out.println("3、将饮料倒入杯中"); } protected abstract void brew(); protected abstract void addCondiments(); }
2、具体子类实现延迟步骤
package com.templatemehod; //具体子类,提供了咖啡制备的具体实现 public class Coffee extends RefreshBeverage { @Override protected void brew() { System.out.println("2、用沸水冲泡咖啡"); } @Override protected void addCondiments() { System.out.println("4、加入糖和牛奶"); } }
package com.templatemehod; //具体子类,提供了制备茶的具体实现 public class Tea extends RefreshBeverage { @Override protected void brew() { System.out.println("2、用80度热水浸泡茶叶5分钟"); } @Override protected void addCondiments() { System.out.println("4、加入柠檬"); } //自定义是否需要挂钩 @Override protected boolean isCustomerWantsCondiments() { return false; } }
3、测试一下
package com.templatemehod; public class RefreshBeverageTest { public static void main(String[] args) { System.out.println("制备咖啡..."); RefreshBeverage b1 = new Coffee(); b1.prepareBeverageTemplate(); System.out.println("咖啡好了..."); System.out.println(" ..........................."); System.out.println("制备茶..."); RefreshBeverage b2 = new Tea(); b2.prepareBeverageTemplate(); System.out.println("茶好了..."); } }
测试结果: 在泡茶的时候,不加入调料
制备咖啡...
1、将水煮沸
2、用沸水冲泡咖啡
3、将饮料倒入杯中
4、加入糖和牛奶
咖啡好了...
...........................
制备茶...
1、将水煮沸
2、用80度热水浸泡茶叶5分钟
3、将饮料倒入杯中
茶好了...
模板方法模式总结:
模板方法模式的实现要素
从类来看,抽象基类和具体子类
抽象基类中,
1)有基本方法,boilWater(),pourInCup(),对子类而言是相同的,具有共性的,直接在抽象基类中定义的方法
2)有抽象方法,brew(), addCondiments(),只知道具体原则,而不知道具体的实现细节,需要延迟到子类中去进行实现的步骤
3)可选的钩子,钩子函数在基类中提供的默认或者为空的实现,以供给子类决定是否挂载或者如何挂载,从而影响算法实现的方法
4)Template方法(final),将基本方法,抽象方法,钩子方法汇按照业务逻辑需求汇总成模板方法。注意:声明为final,不能为子类所覆写。
5)子类可以替换掉父类中的可变逻辑,但不能改变整体逻辑结构。
具体子类中,
1)实现基类中的抽象方法,提供个性化的具体实现。如冲泡咖啡,浸泡茶叶
2)覆盖钩子方法,个性化影响算法的局部的行为。如泡茶时不加入柠檬,覆写钩子方法;冲咖啡的时候要加入糖和牛奶,没有覆写钩子方法。
模板方法的实现要素:
准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模板方法。
模板方法的适用场景:
1)算法或操作遵循相似的逻辑
2)重构时(把相同的代码抽取到父类中,以便于代码复用,共性抽象模板方法,个性交给子类实现)
3)重要,复杂的算法,核心算法设计为模板算法
模板方法的优点:
1)封装性好
2)复用性好(如办理银行业务)
3)屏蔽细节
4)便于维护
模板方法的缺点:
继承,单继承语言,一个类只能extends一个父类,通过模板方法模式中引入新的继承,会有问题。
行业案例:
电信运行商系统,分析处理各种日志
需求分析:种类繁多数量巨大的日志,先抽取共性,再获得规律性的东西
规律:1,2,3,5都是一样的,只有4不同,所以可以应用模板方法模式
1、获得文件 --抽象基类
2、打开文件 --抽象基类
3、读取日志结构 --抽象基类
4、处理单行日志 --延迟到子类实现
5、清理工作 --抽象基类