# 模板方法模式

# 现实案例

  • 现在咖啡馆可以同时做 “咖啡” 和 “茶”;
  • 做咖啡和做茶的步骤很相似,只是在一些细节上不同;
  • 2020-2-22-16-16-19.png
  • 我们可以从咖啡和茶中抽象出一个 “咖啡因饮料” 超类;
  • 超类中有一个 prepareDrink 方法,里面定义了一系列步骤,这些步骤同时适用于 “咖啡” 和 “茶”;
  • 然后我们在定义 “咖啡类” 和 “茶类” 时,再分别去定义各自步骤的实现;
  • 在这种实现中,我们抽象出了饮料的冲泡方法,把它放在了超类;
public abstract class CaffeineBeverage {
  // 这一整套流程,在所有子类中都适用;
  // 但是其中的几个步骤的具体实现不同,我们让子类自己去实现;
  // 定义成 final 方法,是不想让子类去覆盖这个方法
  final void prepareDrink() {
    boilWater();
    brew();
    pourInCup();
    addConditments();
  }

  // 因为咖啡和茶处理这两个步骤的方法不同;
  // 所以我们定义成抽象,然后让两个子类自己分别去实现;
  abstract void brew();
  abstract void addConditments();

  // 这两个步骤在所有的子类中都是相同的,
  // 所以我们直接定义出来,然后让子类去继承
  void boilWater() {
    System.out.println("煮开水");
  }

  void pourInCup() {
    System.out.println("倒进杯子里");
  }
}
// 分别在子类中去实现各自不同的步骤
public class Tea extends CaffeineBeverage {
  public void brew() {
    System.out.println("煮茶叶");
  }

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

public class Coffee extends CaffeineBeverage {
  public void brew() {
    System.out.println("煮咖啡");
  }

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

# 模板方法模式

  • 模板方法模式在一个方法中定义一个算法的骨架(模板),其中一些方法实现由超类提供,剩下的方法实现由子类提供
  • 模板方法使得子类可以在不改变算法结构的情况下,重现定义算法中的某些步骤
  • 在上面 👆 的例子中,prepareDrink 方法就是一个 “模板方法”;
    • 它是一个方法;
    • 它用作一个算法的模板。在上面例子中,算法是用来做饮料的;
    • 算法中涉及的某些方法,由超类处理;
    • 某些方法由子类处理;

2020-2-22-16-34-36.png

  • 通过使用模板方法模式,超类定义了算法结构,它保护了这个算法;
  • 减少了子类之间重复的代码。使代码复用率最大化;
  • 算法只定义在超类中,维护起来简单;
  • 超类专注于算法本身,而子类去负责提供完整的实现;

# 钩子

  • 钩子是一种被声明在抽象类中的方法。但是只有空或者默认的实现
  • 钩子的存在,让子类有能力对算法的不同点进行挂钩
    • 意思是有了钩子,子类就可以决定要不要覆盖这个方法;
    • 如果覆盖了,到时候就调用子类自己实现的方法;
    • 如果不覆盖,就用超类中默认的实现;

2020-2-22-16-43-37.png

2020-2-22-16-44-51.png

  • 通过在子类中重写钩子方法,去覆盖超类中的钩子;
  • 子类就有能力控制超类算法中,各个钩子的行为了,这被称作 “挂钩”;

  • 当子类 “必须” 提供对算法中某个方法的实现时,就用 “抽象方法”
  • 当算法中某个步骤是 “可选的”,就用钩子。让子类可以自行选择是否实现这个钩子;
  • 在设计算法时,不要把步骤切的太细;要不然子类实现起来也很麻烦。但如果粒度太粗,算法也会缺少弹性,要看情况自行取舍;
  • 如果某些步骤是可选的,就把它实现成钩子,而不是抽象方法。这样可以让子类的负担减轻;

# 好莱坞原则

  • 好莱坞原则底层组件不要调用高层组件,让高层组件调用底层组件
  • 在此原则下,我们允许底层组件将自己挂钩到高层组件上,高层组件会自己决定什么时候,以什么样的方式去使用这些底层组件;
  • 好莱坞原则可以防止 “依赖腐败”,当高层组件依赖底层组件,而底层组件又依赖高层组件时,依赖腐败就发生了;
  • 依赖腐败发生时,代码的可维护性,可扩展性降低,耦合性增高。代码的理解难度极具增加;

  • 在使用模板方法模式时,我们就应用了好莱坞模式;
  • 子类给超类提供了算法中一些步骤的实现;
  • 超类定义了算法,超类会在需要时,调用子类中实现的方法;
  • 客户最后依赖的是超类中定义的算法,而不是子类中具体的实现;

2020-2-22-17-2-21.png

  • 其实在 👆 做饮料的例子中,我们发现子类还是会调用从超类继承来的方法的。但是我们最主要的目的是避免子类和超类间的 “环装依赖”
上次更新: 8/22/2020, 8:14:09 AM