设计模式--模板方法模式

模板方法模式

业务流程固定,只是具体某些步骤存在可变化的部分。如银行办理业务流程

银行办理业务流程:

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、清理工作 --抽象基类

原文地址:https://www.cnblogs.com/nikey/p/6496619.html