将相同的流程作为一个抽象类

  1. Beverage.prototype.init = function(){
  2. this.boilWater();
  3. this.brew();
  4. this.pourInCup();
  5. this.addCondiments();
  6. };

使用场景

从大的方面来讲,模板方法模式常被架构师用于搭建项目的框架,架构师定好了框架的骨架, 程序员继承框架的结构之后,负责往里面填空,比如 Java 程序员大多使用过 HttpServlet 技术来 开发项目。

比如:
vue 或react的组件包含的生命周期

  • created
  • mounted
  • beforeupdate
  • beforeDestory

    react

  • componentWillMound

  • componentDidMound
  • sholdComponentUpdate
  • componentDidUpdate
  • componentWillUnMount

钩子方法

通过模板方法模式,我们在父类中封装了子类的算法框架。这些算法框架在正常状态下是适 用于大多数子类的,但如果有一些特别“个性”的子类呢?比如我们在饮料类 Beverage 中封装了 饮料的冲泡顺序:

  • 把水煮沸
  • 用沸水冲泡饮料
  • 饮料倒进杯子
  • 加调料

这 4 个冲泡饮料的步骤适用于咖啡和茶,在我们的饮料店里,根据这 4 个步骤制作出来的咖 啡和茶,一直顺利地提供给绝大部分客人享用。但有一些客人喝咖啡是不加调料(糖和牛奶)的。 既然 Beverage 作为父类,已经规定好了冲泡饮料的 4 个步骤,那么有什么办法可以让子类不受这 个约束呢?

钩子方法 可以解决这个问题,放置钩子是隔离变化的一种常见手段。我们在父 类中容易变化的地方放置钩子,钩子可以有一个默认的实现,究竟要不要“挂钩”,这由子类自 行决定。钩子方法的返回结果决定了模板方法后面部分的执行步骤,也就是程序接下来的走向,这样一来,程序就拥有了变化的可能。

在这个例子里,我们把挂钩的名字定为 customerWantsCondiments,接下来将挂钩放入 Beverage 类,看看我们如何得到一杯不需要糖和牛奶的咖啡,代码如下:

  1. var Beverage = function(){};
  2. Beverage.prototype.boilWater = function(){
  3. console.log( '把水煮沸' );
  4. };
  5. Beverage.prototype.brew = function(){
  6. throw new Error( '子类必须重写 brew 方法' );
  7. };
  8. Beverage.prototype.pourInCup = function(){
  9. throw new Error( '子类必须重写 pourInCup 方法' );
  10. };
  11. Beverage.prototype.addCondiments = function(){
  12. throw new Error( '子类必须重写 addCondiments 方法' );
  13. };
  14. Beverage.prototype.customerWantsCondiments = function(){
  15. return true; // 默认需要调料
  16. };
  17. Beverage.prototype.init = function(){
  18. this.boilWater();
  19. this.brew();
  20. this.pourInCup();
  21. if ( this.customerWantsCondiments() ){ // 如果挂钩返回 true,则需要调料
  22. this.addCondiments();
  23. }
  24. };
  25. var CoffeeWithHook = function(){};
  26. CoffeeWithHook.prototype = new Beverage();
  27. CoffeeWithHook.prototype.brew = function(){
  28. console.log( '用沸水冲泡咖啡' );
  29. };
  30. CoffeeWithHook.prototype.pourInCup = function(){
  31. console.log( '把咖啡倒进杯子' );
  32. };
  33. CoffeeWithHook.prototype.addCondiments = function(){
  34. console.log( '加糖和牛奶' );
  35. }
  36. CoffeeWithHook.prototype.customerWantsCondiments = function(){
  37. return window.confirm( '请问需要调料吗?' );
  38. };
  39. var coffeeWithHook = new CoffeeWithHook();
  40. coffeeWithHook.init();