示例
以冲泡咖啡喝茶为例:
咖啡冲泡法:
- 把水煮沸
- 用沸水冲泡咖啡
- 把咖啡倒进杯子
- 加糖加牛奶
茶冲泡法:
- 把水煮沸
- 用沸水浸泡茶叶
- 把茶倒进杯子
- 加柠檬
茶和咖啡的冲泡方法非常相似。
Coffee.java
1 | public class Coffee { |
Tea.java
1 | public class Tea { |
抽象
可以看到上面的Coffee和Tea中有重复代码,都含有prepareRecipe()、boilWater()和pourInCup()方法,并且boilWater()和pourInCup()实现完全一样,prepareRecipe()在两者中的实现有所不同。因此,我们可能想到的抽象设计可能是如下:
进一步封装冲泡法,茶和咖啡的冲泡法都采用了相同的算法。
- 把水煮沸
- 用热水泡咖啡或茶
- 把饮料倒进杯子
- 在饮料内加入适当的调料
因此,我们可以将prepareRecipe()也进行抽象。
咖啡因饮料基类:
1 | public abstract class CaffeineBeverage { |
Coffee.java
1 | public class Coffee extends CaffeineBeverage { |
Tea.java
1 | public class Tea extends CaffeineBeverage { |
我们做了什么:
模板方法模式
模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
定义:
模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
对模板方法进行挂钩
钩子是一种声明在抽象类中的方法,但只有空的或默认实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。
钩子使用方式一:条件语句
为了使用钩子,我们要在子类中覆盖它。在这里,钩子控制了咖啡因饮料是否执行某部分算法。说的明确一点,就是饮料中是否要加调料。
QA
创建模板方法时,什么时候用抽象方法,什么时候用钩子?
当子类必须提供算法中某个方法或步骤的实现时,就使用抽象方法。如果这个步骤是可选的,就用钩子。使用钩子的目的是什么?
- 钩子可以让子类实现算法的可选部分。
- 让子类能够有机会对模板方法中某些即将发生的步骤做出反应。
好莱坞原则
别调用我们,我们会调用你。
好莱坞原则可以给我们一种防止“依赖腐败”的方法。当高层组件依赖低层组件,低层组件又依赖高层组件,高层组件又依赖边侧组件,边侧组件又依赖低层组件时,依赖腐败就发生了。
在好莱坞原则下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我,我会调用你”。
源码:https://github.com/chentianming11/design-pattern
template包!