设计模式——模板方法模式

五陵年少金市东,银鞍白马渡春风。这篇文章主要讲述设计模式——模板方法模式相关的知识,希望能为你提供帮助。
模板方法模式定义在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
例子举一个生活中的场景:泡咖啡和泡茶。
泡咖啡的步骤为:

  1. 把水煮沸
  2. 用沸水冲泡咖啡
  3. 把咖啡倒进杯子
  4. 加糖和牛奶
泡茶的步骤为:
  1. 把水煮沸
  2. 用沸水浸泡茶叶
  3. 把茶倒进杯子
  4. 加柠檬
我们用java类来实现这两个过程,代码如下
//咖啡类 public class Coffee void prepareRecipe() boilWater(); brewCoffeeGrinds(); pourInCup(); addSugarAndMilk(); private void addSugarAndMilk() System.out.println("Adding Sugar and Milk"); private void pourInCup() System.out.println("Pouring into cup"); private void brewCoffeeGrinds() System.out.println("Dripping coffee through filter"); private void boilWater() System.out.println("Boiliing water"); //茶类 public class Tea void prepareRecipe() boilWater(); steepTeaBag(); pourInCup(); addLemon(); private void addLemon() System.out.println("Add Lemon"); private void pourInCup() System.out.println("Pouring into cup"); private void steepTeaBag() System.out.println("Steeping the tea"); private void boilWater() System.out.println("Boiling water");

在这两个类中,我们可以看到,$boilWater()$ 方法和 $pourIncup()$ 方法是茶和咖啡类共有的,而剩下的两个方法是茶类和咖啡类专有的。因此我们可以设计一下,把共同的部分抽取出来,放到一个基类中。
而不同的部分虽然不能被抽取出来,但是他们本质上其实是一样的,只是应用到了不同的饮料上。这样,我们就可以从这两个子类中再次抽象一下,把冲泡方法和加调料方法抽象出来。
父类代码如下
public abstract class CaffeineBeverage final void prepareRecipe() boilWater(); brew(); pourInCup(); addCondiments(); abstract void brew(); abstract void addCondiments(); void boilWater() System.out.println("Boiling water"); void pourInCup() System.out.println("Pouring into cup");

这里咖啡因饮料是一个抽象类。
我们用同一个$prepareRecipe()$方法来处理茶和咖啡,$prepareRecipe()$被声明为$final$,因为我们不希望子类覆盖这个方法。
因为咖啡和茶的处理方法不同,因此我们把这两个方法声明为抽象方法,剩余的东西留给子类去实现。
继承咖啡因饮料类来实现咖啡和茶类
//咖啡类 public class Coffee extends CaffeineBeverage @Override void brew() System.out.println("Dripping Coffee through filter"); @Override void addCondiments() System.out.println("Adding Sugar and Milk"); //茶类 public class Tea extends CaffeineBeverage @Override void brew() System.out.println("Steeping the tea"); @Override void addCondiments() System.out.println("Adding Lemon");

上述就是一个模板方法模式的经典案例,而其中的$prepareRecipe()$就是一个模板方法。在这个模板中,算法内的每一个步骤都被一个方法代表了,某些方法是由这个类来实现的,而某些方法是由子类实现的,子类实现的方法必须在父类中声明为抽象方法。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
对模板方法进行挂钩【设计模式——模板方法模式】钩子是一种被声明在抽象类中的方法,但是只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。
使用钩子的例子
public abstract class CaffeineBeverageWithHook void prepareRecipe() boilder(); brew(); pourInCup(); if (customerWantsCondiments()) addCondiments(); abstract void brew(); abstract void addCondiments(); void boilder() System.out.println("Boilding water"); void pourInCup() System.out.println("Pouring into cup"); boolean customerWantsCondiments() return true;

在上述抽象类中,我们加上了一个条件语句,而该条件是否成立是由一个具体方法$customerWantsCondiments()$决定的,如果顾客想要加调料,这是我们才调用$addCondiments()$
在父类中,我们定义出这个方法,通常是空的缺省实现。这个方法只会返回$true$,不做别的事。这就是一个钩子,子类可以覆盖这个方法,但不一定要这么做。
而为了使用钩子,我们就需要在子类中覆盖它。具体实现由业务逻辑决定。在这里,钩子控制了饮料中是否加调料,因此,实现逻辑可以是简单的询问顾客的输入做判断即可。
实现
public class CoffeeWithHook extends CaffeineBeverageWithHook @Override public void brew() System.out.println("Dripping Coffee through filter"); @Override public void addCondiments() System.out.println("Adding Sugar and Milk"); @Override public boolean customerWantsCondiments() String answer = getUserInput(); if (answer.toLowerCase().startsWith("y")) return true; else return false; private String getUserInput() Scanner sc = new Scanner(System.in); System.out.println("Would you like milk and sugar with your coffee (y / n) ?"); String answer = sc.nextLine(); return answer;

上述咖啡类就继承了带有钩子的父类,并且重写了钩子方法,对用户的输入做了判断,已达到钩子的效果。
写一个测试类来测试一下
public class CoffeeTest public static void main(String[] args) CoffeeWithHook coffeeWithHook = new CoffeeWithHook(); coffeeWithHook.prepareRecipe();

执行结果
设计模式——模板方法模式

文章图片


    推荐阅读