设计模式之模板方法模式

来源:互联网 发布:unity 删除数组 编辑:程序博客网 时间:2024/06/09 13:10

1. 模板方法模式概述

在现实生活中,很多事情都会有一些同样的抽象步骤,只是具体的实现不一样而已;比如我们去银行办理业务;
1:进门取号;
2:填写单据;
3:等待叫号;
4: 窗口办理;
这里我们的填写单据是一个抽象的方法,因为我们办理的业务不同,所填写的单据也会不一样;
再比如我们做程序员的喜欢喝咖啡,也有喜欢喝茶的,那么泡咖啡喝泡茶其实就有很大相同的步骤;

加工流程:

咖啡冲泡法:1.把水煮沸、2.用沸水冲泡咖啡、3.把咖啡倒进杯子、4.加糖和牛奶

茶冲泡法:   1.把水煮沸、2.用沸水冲泡茶叶、3.把  茶 倒进杯子、4.加蜂蜜

那么根据上面的共性我们可以理解为:

模板方法模式:定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

Template Method Pattern:  Define the skeleton of an algorithm in an  operation, deferring some steps to subclasses. Template Method lets  subclasses redefine certain steps of an algorithm without changing the  algorithm's structure.

2. 模板方法模式结构与类图

模板方法模式结构比较简单,其核心是抽象类和其中的模板方法的设计,其结构如图2所示:

2模板方法模式结构图

      由图2可知,模板方法模式包含如下两个角色:

       (1) AbstractClass(抽象类)在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。

      (2) ConcreteClass(具体子类)它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。

3. 模板方法模式的具体实现

这里我们以泡咖啡和泡茶为例子:
我们都知道不论你是泡茶还是泡咖啡肯定是要先去热水,然后再用沸水去冲泡;
这里我先创建一个模板方法:
/* * 抽象基类,为所有子类提供一个算法框架 *  * 提神饮料 */public abstract class RefreshBeverage {/* * 制备饮料的模板方法 * 封装了所有子类共同遵循的算法框架 */public final void prepareBeverageTemplate(){//步骤1 将水煮沸boilWater();//步骤2 泡制饮料brew();//步骤3 将饮料倒入杯中pourInCup();if(isCustomerWantsCondiments()){//步骤4 加入调味料addCondiments();}}/* * Hook, 钩子函数,提供一个默认或空的实现 * 具体的子类可以自行决定是否挂钩以及如何挂钩 * 询问用户是否加入调料 */ protected boolean isCustomerWantsCondiments() {return true;}/* * 基本方法,将水煮沸 */private void boilWater() {System.out.println("将水煮沸");}/* * 基本方法,将饮料倒入杯中 */private void pourInCup() {System.out.println("将饮料倒入杯中");}/* * 抽象的基本方法,泡制饮料 */protected abstract void brew();/* * 抽象的基本方法, 加入调味料 */protected abstract void addCondiments();}

这里我们一定要把这个模板方法声明为final的,因为这样表示不可被子类改变;
然后再去创建我们的咖啡类;实现具体泡咖啡的过程:
* * 具体子类,提供了咖啡制备的具体实现 */public class Coffee extends RefreshBeverage {@Overrideprotected void brew() {System.out.println("用沸水冲泡咖啡");}@Overrideprotected void addCondiments() {System.out.println("加入糖和牛奶");}}
再去创建一个泡茶的过程:
/* * 具体子类,提供了制备茶的具体实现 */public class Tea extends RefreshBeverage {@Overrideprotected void brew() {System.out.println("用80度的热水浸泡茶叶5分钟");}@Overrideprotected void addCondiments() {System.out.println("加入柠檬");}@Override/* * 子类通过覆盖的形式选择挂载钩子函数 * @see com.imooc.pattern.template.RefreshBeverage#isCustomerWantsCondiments() */protected boolean isCustomerWantsCondiments(){return false;}}

这里你可以看到,我们两个方法都有一个钩子方法,因为不论泡咖啡还是泡茶,都可以不加调料,也可以加调料,这里我们使用钩子方法,让子类函数自己去实现勾或者不勾加调料的方法;
最后编写我们的测试用例:
public class RefreshBeverageTest {public static void main(String[] args) {System.out.println("制备咖啡...");RefreshBeverage b1 = new Coffee();b1.prepareBeverageTemplate();System.out.println("咖啡好了!");System.out.println("\n******************************************");System.out.println("制备茶...");RefreshBeverage b2 = new Tea();b2.prepareBeverageTemplate();System.out.println("茶好了!");}}
模板方法总结:
模板方法应该算是一个比较容易理解,比较简单的设计模式了,他的使用场景非常丰富,当我们重构时把相同的算法的代码抽取的父类中,让一些具体的算法延迟到子类中去实现;还有算法或操作都遵循相似的逻辑的时候也可以使用模板方法;

模板方法模式的优点:
1:封装性好;
2:重要性高;
3:屏蔽细节;
4:便于维护;

具体的使用场景:
这里给大家分享一个模板方法的具体使用场景:
一个大型的仓储系统,离不开大量数据的导入系统;
这里经过分析业务,抽取共性,找出规律;
我们发现不论需求怎么变,始终离不开下面几步;
1:获得文件
2:解析文件
3:读取文件结构
4:业务数据校验
5:导入统计
这里面的第四步的变化是最大的,因为每个货主的不同,或者每个公司的不同,他的到不数据校验是不一样的,所以这里就使用了模板方法模式;

1 0
原创粉丝点击