设计模式系列之模板方法模式【九】

1. 定义

父类定义一个操作算法中的框架(调用哪些方法及调用顺序),某些特定方法由子类实现。

2. 示例

《Head First设计模式》中关于模板方法模式用的例子就非常的恰当,是冲泡咖啡和茶的大致工序相似,只是某些具体实现细节不一样。

2.1 父类定义骨架

父类中需要定义所有的模板方法,并且定义需要执行的流程。泡茶或者咖啡都需要有以下步骤:

  1. 将水煮沸;
  2. 冲泡(需要子类实现),因为茶或者咖啡冲泡的方式不太一样,比如茶一般需要先加少量水进行冲泡;
  3. 将水倒入杯中
  4. 添加调味料(需要子类实现) 比如柠檬茶则还需要加入一些柠檬水或者柠檬
package com.linyf.demo.pattern.model;

/**
 * 模板基类(咖啡或茶的冲泡步骤模板)
 */
public abstract class CaffeineBeverage {
    /**
     *  一般都用final修饰,不允许改变{咖啡或茶的冲泡}执行步骤
     */
    public final void prepareRecipe() {

        boilWater();

        brew();

        pourInCup();

        /**
         *  设置一个回调钩子
         */
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    /**
     *  将水煮沸
     */
    public void boilWater() {
        System.out.println("将水煮沸!");
    }

    /**
     *  冲泡(需要子类实现)
     */
    public abstract void brew();

    /**
     *  将水倒入杯中
     */
    public void pourInCup() {
        System.out.println("将沸腾的水倒入杯中!");
    }

    /**
     *  添加调味料(需要子类实现)
     */
    public abstract void addCondiments();

    public boolean customerWantsCondiments() {
        return true;
    }
}

2.2 子类实现特殊的处理

package com.linyf.demo.pattern.model;

public class Coffee extends CaffeineBeverage{

    public Coffee(){
        super.prepareRecipe();
    }

    @Override
    public void brew() {
        System.out.println("咖啡直接冲泡!");
    }

    @Override
    public void addCondiments() {
        System.out.println("加糖。。。");
    }

    @Override
    public boolean customerWantsCondiments() {
        return true;
    }

}
package com.linyf.demo.pattern.model;

public class Tea extends  CaffeineBeverage {
    public Tea(){
        super.prepareRecipe();
    }

    @Override
    public void brew() {
        System.out.println("泡茶需要将80度左右的水将茶叶泡开了!");
    }

    @Override
    public void addCondiments() {

    }

    @Override
    public boolean customerWantsCondiments() {
        return false;
    }
}

2.3 测试

package com.linyf.demo.pattern.model;

public class Test {
    public static void main(String[] args) {
        System.out.println("tea ......");
        CaffeineBeverage tea = new Tea();
        System.out.println("tea is OK !");

        System.out.println("---------------------------------------------");

        System.out.println("Coffee ......");
        CaffeineBeverage coffee = new Coffee();
        System.out.println("Coffee is OK !");
    }
}

执行结果:
设计模式系列之模板方法模式【九】_第1张图片

3. 优点、缺点、注意事项、使用场景

3.1 优点

  • 开发人员在开发时,只需要考虑方法的实现。不需要考虑方法在何种情况下被调用。
  • 实现代码复用,父类的一些方法直接被子类使用。
  • 既统一了算法,也保证了很大的灵活性(除了子类要实现的特定方法,其他方法及方法调用顺序都在父类中预先写好)

3.2 缺点

每一个不同的实现都需要一个子类来实现,导致类的数量增加,系统更庞大

3.3 注意事项

一般模板方法要加上 final 修饰符,防止子类重写;

子类很多时候需要对方法进行特殊处理,分为两种情况:

  • 抽象方法:父类中的是抽象方法,子类必须覆盖
  • 钩子方法:父类中是一个空方法,子类继承了默认也是空的

3.4 使用场景

当我们要完成某个过程,该过程有一系列步骤,而且这些步骤基本相同,只有个别步骤的实现不同,可以考虑使用模板方法模式

4. 在Spring源码中的应用

模板方法模式在 Spring IOC 容器初始化的时候有所应用,看一下 AbstractApplicationContext 中的 refresh 方法,它就是一个模板方法,里面调用了一系列方法,有已实现的具体方法,有未实现的抽象方法,也有空的钩子方法:
设计模式系列之模板方法模式【九】_第2张图片
在这里插入图片描述

设计模式系列之模板方法模式【九】_第3张图片
设计模式系列之模板方法模式【九】_第4张图片

你可能感兴趣的:(java,设计模式)