设计模式——模板模式(Template Pattern)

  在读Spring源码的时候,发现Spring代码中运用了大量的模板模式,比如根据文件系统目录加载配置文件(FileSystemXmlApplicationContext),类路径加载配置文件(ClassPathXmlApplicationContext),以及根据项目上下文目录(XmlWebApplicationContext)加载配置文件。这个在加载的过程中就使用了模板设计模式,所以特意去学习了一下模板设计模式,从而更好的理解源码。

1.模板设计模式在书中的定义

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

  我的翻译就是:完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。

2.举个例子来说

  小张的团队最近接受一个需求,实现实现一家咖啡店的冲泡咖啡和茶的冲泡自动化。之前这家咖啡店都是由咖啡师傅手动进行调制咖啡和茶。现在咖啡店需要引入自动化的点单和调制饮料的系统,小张负责实现调制饮料的功能。

  咖啡师傅手工冲泡咖啡和茶的流程:

  冲泡咖啡:

         把水煮沸

              用沸水冲泡咖啡

              把咖啡倒入杯子

              加糖和牛奶

   冲泡茶:

            把水煮沸

            用沸水冲泡茶叶

            把茶倒入杯子

            加柠檬

  使用模板模式实现

  我们首先看一个类图

  

  代码实现

package xuelongjiang.designpartten.templatemethod;

/**
 *
 * 模板方法
 *
 *
 */
abstract   public class CaffeineBeverage {

    //算法。 抽象类的算法是final 的不允许被子类修改
     public final  void   prepareRecipe(){

         //算法的具体步骤
         boilWater(); //烧水
         brew();//冲泡
         pourInCup();// 把饮料倒入杯子
         if(hook()){
             addCondiments();// 加调料
         }

    }


   public  abstract  void brew();

   public   abstract  void addCondiments();


    public void boilWater(){

        System.out.println("烧水");
    }


    public  void pourInCup(){
        System.out.println("把饮料倒入杯子");
    }

    /**
     *  钩子,具体实现可以对算法步骤做一些控制
     *
     * @return
     */
    public boolean hook(){
        return true;
    }

}

  可以看到我们在冲泡咖啡/茶的抽象类中有一个hook方法,这个方法就是钩子方法。默认返回true,如果冲泡咖啡默认是加调料的那么子类就不用重写hook方法。

  咖啡类

package xuelongjiang.designpartten.templatemethod;

/**
 *咖啡类
 */
public class Coffee extends  CaffeineBeverage {


    @Override
    public void brew() {
        System.out.println("用沸水冲泡咖啡粉");
    }

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

    @Override
    public boolean hook() {
        return super.hook();
    }
}

  茶类

package xuelongjiang.designpartten.templatemethod;

/**
 * 茶类
 */
public class Tea extends  CaffeineBeverage {


    @Override
    public void brew() {
        System.out.println("用沸水侵泡茶叶");
    }

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


    @Override
    public boolean hook() {
        return super.hook();
    }
}

  测试类

package xuelongjiang.designpartten.templatemethod;
/**
 * 测试类
 */
public class TemplateMethodTest {


    public static void main(String[] args) {
        CaffeineBeverage caffeineBeverage = new Tea();
        caffeineBeverage.prepareRecipe();

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

        caffeineBeverage = new Coffee();
        caffeineBeverage.prepareRecipe();


    }
}

  

  要点    

    好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。(即高层组件对低层组件的方式是:别调用我们,我们会调用你们)。

    模版方法定义了算法的步骤,把这些步骤的实现延迟到了子类。

    模版方法模式为我们提供了一种代码复用的重要技巧。

    模版方法的抽象类可以定义具体方法、抽象方法和钩子。

    抽象方法由子类实现。

    为了防止子类改变模版方法中的算法,可以将模版方法声明为final

———————
代码部分参考:https://blog.csdn.net/u013565163/article/details/79285617