模板方法模式(Template method Pattern)
概念:
模板方法模式 在一个方法中定义一个算法的 骨架,而将一些步骤延迟到子类。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
组成:
templateMethod(模板方法):定义了某个算法的模板顺序或一组步骤,其中任何步骤都可以是抽象的,由子类来实现。
primitiveOperation1(抽象步骤方法):抽象类中声明,由子类各自实现。
hook(钩子函数):钩子是一种被声明在抽象类中的方法,但只有空的和默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩由子类自行决定。当我们的子类 “必须” 提供某个方法或步骤的实现时,就使用抽象方法。如果算法的这个部分是可选的,就用钩子。如果是钩子的化,子类可以选择实现这个钩子,但不强制这么做。简单来说 可选的步骤 一般实现为钩子函数。
例子:
咖啡店里的咖啡和茶制作时都有类似的步骤,首先加热水,然后放入茶或咖啡搅拌(冲泡),然后将饮料倒入杯子,最后加调料。这些步骤是固定的,但固定的步骤中冲泡和加调料两者分别是不同的。我们现在通过模板方法模式来实现它。
抽象模板方法
public abstract class CoffeeShopBeverage {
//模板方法,制作步骤
final void prepareRecipe() {
boidWater();
brew();
pourInCup();
if (isAddCondiments()) {
addCondiments();
}
}
//冲泡
abstract void brew();
//加调料
abstract void addCondiments();
//固定步骤
void boidWater() {
System.out.println("加热水...");
}
void pourInCup() {
System.out.println("倒入杯中...");
}
//钩子函数,来决定是否加调料
boolean isAddCondiments() {
return true;
}
}
咖啡类
public class Coffee extends CoffeeShopBeverage {
@Override
void brew() {
System.out.println("放入咖啡...");
}
@Override
void addCondiments() {
System.out.println("加奶和糖...");
}
}
茶类
public class Tea extends CoffeeShopBeverage {
@Override
void brew() {
System.out.println("放入茶叶...");
}
@Override
void addCondiments() {
System.out.println("加柠檬...");
}
//覆盖钩子函数,默认不加柠檬
@Override
boolean isAddCondiments() {
System.out.println("什么也不加...");
return false;
}
}
测试类:
public class Drink {
public static void main(String[] args) {
Coffee coffee = new Coffee();
System.out.println("Coffee..");
coffee.prepareRecipe();
Tea tea = new Tea();
System.out.println("\nTea...");
tea.prepareRecipe();
}
}
适用场景:
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
- 控制子类扩展。模板方法只在特定点调用“ hook”操作 ,这样就只允许在这些点进行扩展。
优缺点:
优点:
- 模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。
- 模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。
- 模板方法模式导致一种反向的控制结构,这种结构有时被称为“好莱坞法则” ,即“别找我们,我们找你”通过一个父类调用其子类的操作(而不是相反的子类调用父类),通过对子类的扩展增加新的行为,符合“开闭原则”。
缺点:
- 每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。
补充:好莱坞原则
好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。
好莱坞原则是用在创建框架或组件上的一种技巧,好让底层组件能够被挂钩进计算中,而且又不会让高层组件依赖底层组件。创建一个有弹性的设计,允许底层结构能够相互操作,而又防止其他类太过依赖它们。
参考:
《Head first 设计模式》