模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤
-
- 模板就是基类中的一个方法,包含一组步骤,任何一个步骤都可以是抽象的,可以在保证算法结构不变的情况下由子类实现具体某个步骤
类图:
特点:
- 模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现
- 基类主导一切,它拥有这个算法,知道这个算法的执行步骤,并保护这个算法,基类关在与算法本身步骤,而由子类提供完整实现
- 算法只存在于基类便于修改,算法中相同的部分由基类实现,将代码复用最大化
- 模板方法的抽象类可以定义具体方法(final)、抽象方法、钩子
注意:
- 模板方法应该被定义为final,以防止被子类覆盖
- 模板方法中具体某一步也可以定义为final,以防止被子类覆盖
默认不做事情的方法hook:
- 只有空或者默认的实现,子类可以自行决定是否覆盖
- 可以用作条件控制,影响抽象类中算法的流程
- 子类必须实现时使用抽象方法,是否实现可选时使用hook方法
好莱坞原则:别调用我们,我们会调用你
- 允许底层组件自己挂钩到系统上,但是什么时候使用怎么使用这些组建由高层组建决定
- 好莱坞原则vs依赖倒置原则
- 依赖倒置原则教我们避免使用具体的类,使用接口抽象编程
- 好莱坞原则运用在框架上,底层组件依靠挂钩参与运算,但又不会让高层组建依赖于底层组建
- 两者的目的均是解耦、避免依赖,创造有弹性的设计
JavaAPI中应用
- Arrays.sort()函数通过被排序数组对象的compareTo()函数填补模板方法的比较方法
模板方法模式vs策略模式:
- 策略使用组合,模板方法使用继承
- 策略定义算法家族,算法可以相互替换;模板方法定义算法大纲,具体步骤细节可以在子类实现
举例:
制作咖啡因饮品的模板方法基类
1 public abstract class CaffeineBeverage { 2 3 final void prepareRecipe() { 4 boilWater();//把水煮沸 5 brew();//用热水泡咖啡或茶 6 pourInCup();//把饮料(咖啡或茶)倒进杯子 7 addCondiments();//加入调料 8 } 9 10 abstract void brew(); 11 12 abstract void addCondiments(); 13 14 void boilWater() { 15 System.out.println("Boiling water"); 16 } 17 18 void pourInCup() { 19 System.out.println("Pouring into cup"); 20 } 21 }
Coffee实现
1 public class Coffee extends CaffeineBeverage { 2 public void brew() { 3 System.out.println("Dripping Coffee through filter"); 4 } 5 public void addCondiments() { 6 System.out.println("Adding Sugar and Milk"); 7 } 8 }
使用钩子的模板方法
1 void prepareRecipe() { 2 boilWater(); 3 brew(); 4 pourInCup(); 5 //让子类可以决定算法是否执行该步骤 6 //customerWantsCondiments()方法是一个钩子,父类对其其默认实现返回true,即默认执行步骤(4) 7 if (customerWantsCondiments()) { 8 addCondiments(); 9 } 10 }
咖啡类钩方法实现
1 public boolean customerWantsCondiments() { 2 3 String answer = getUserInput(); 4 5 if (answer.toLowerCase().startsWith("y")) { 6 return true; 7 } else { 8 return false; 9 } 10 }