【设计模式】模板方法

一、前言

  最近复习发现AQS使用了模板方法,自定义同步器时需要重写几个AQS提供的模板方法,Spring的DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions() 方法也使用了该设计模式,于是写篇文章加深理解。

模板方法模式的定义: 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

单看这句话可能还没搞懂这个设计模式是干嘛的,下面看一个例子。

二、示例

不用设计模式,冲咖啡和泡茶的两个类分别是下面这样的。

public class Coffee {

  // 冲泡咖啡的算法
   void prepareRecipe(){
    boilWater();
    // 用沸水冲泡咖啡
    brewCoffeeGrinds();
    pourInCup();
    // 加牛奶和糖
    addSugarAndMilk();
  }

  private void addSugarAndMilk() {
  }

  private void pourInCup() {
  }

  private void brewCoffeeGrinds() {
  }

  private void boilWater() {
  }
}


public class Tea {

  // 泡茶的算法
  void prepareRecipe(){
    boilWater();
    // 用沸水浸泡茶叶
    steepTeaBag();
    pourInCup();
    // 加柠檬
    addLemon();
  }

  private void addLemon() {

  }

  private void pourInCup() {

  }

  private void steepTeaBag() {

  }

  private void boilWater() {

  }
}

很容易就可以发现这两个类中有重复代码,boilWater()和pourInCup()都重复了可以提取出来,因为茶和饮料都是咖啡饮料可以定义一个超类CaffeineBeverage。
进一步分析,两种冲泡法其实都用了相同的算法:

  1. 把水煮沸
  2. 用热水泡咖啡或茶
  3. 把饮料倒进杯子
  4. 在饮料里加调料

所以prepareRecipe()也可以抽象成一个,如下:

final void prepareRecipe(){
    boilWater();
    // 交给对应的子类实现
    brew();
    pourInCup();
    // 交给对应的子类实现
    addCondiments();
  }

最后,重构后的代码如下。

咖啡因饮料超类

public abstract class CaffeineBeverage {

  /**
   * 模板方法
   * 定义的算法步骤
   */
  final void prepareRecipe(){
    boilWater();
    brew();
    pourInCup();
    addCondiments();
  }

  /**
   * 添加调料
   */
  protected abstract void addCondiments();


  /**
   * 冲泡
   */
  protected abstract void brew();

  /**
   * 煮开水
   */
  private void boilWater() {
    System.out.println("Boiling water");
  }

  /**
   * 把饮料倒进杯子
   */
  private void pourInCup() {
    System.out.println("Pouring in cup");
  }

}

Tea

public class Tea extends CaffeineBeverage {

  @Override
  protected void addCondiments() {
    System.out.println("Adding Lemon");
  }

  @Override
  protected void brew() {
    System.out.println("Steeping the tea");
  }
}

Coffee

public class Coffee extends CaffeineBeverage {

  @Override
  protected void addCondiments() {
    System.out.println("Adding Sugar and Milk ");
  }

  @Override
  protected void brew() {
    System.out.println("Dripping Coffee through filter");
  }
}

代码的类图就变成了下面的样子。

 

类图
类图

 

道理我懂,可是使用了模板方法模式之后有什么好处吗?

原本的实现模板方法后的实现
Coffee和Tea之间存在重复代码 CaffeineBeverage类实现了代码复用最大化
Coffee和Tea控制了算法 由CaffeineBeverage类主导一切,拥有并保护算法
对算法所做的代码改变,需要修改子类很多地方 算法只存在一个地方,很容易修改
算法的知识和它的实现分散在许多类中 CaffeineBeverage类专注算法本身,而由子类提供完整的实现

三、总结

设计模式这么多种,要做到灵活运用还真是长路漫漫啊。

posted @ 2019-10-06 14:46  烟味i  阅读(171)  评论(0编辑  收藏  举报