模板方法模式.
一、概念
- 模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
- 解析:模板方法模式用来创建一个算法的模板。什么是模板?模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。
- 角色:
1、抽象模板(Abstract Template):定义模板方法,规范了一组步骤,其中父类可以实现其中的某些步骤,需要由子类实现的步骤声明为 abstract。
2、具体模板(Concrete Template):继承抽象父类,实现抽象父类中声明为 abstract 的方法。
二、Demo 实现
我们打算定义一个咖啡因饮料冲泡流程,把流程中相同的步骤放在一起,同时,不同的饮料还能有自己的具体实现。
1、抽象模板
public abstract class CaffeineBeverage {
/**
* @Description
* 1、模板方法,定义算法的步骤。
* 2、我们一般把模板方法定义成 final,不希望子类覆盖。
*/
public final void prepareRecipe() {
//1、把水煮沸
boilWater();
//2、用热水泡饮料(具体什么饮料未知)
brew();
//3、把饮料倒进杯子
pourIncup();
//4、往饮料中添加调料(具体什么调料未知)
if (wantAddCondiments()) {
addCondiments();
}
}
/**
* @Description 需要子类提供实现的步骤定义为抽象
*/
protected abstract void brew();
protected abstract void addCondiments();
/**
* @Description 由父类提供实现,又不希望被子类重写的,封装成 private。
*/
private void boilWater() {
System.out.println("Boiling water");
}
private void pourIncup() {
System.out.println("Pouring into cup");
}
/**
* @Description 钩子方法
* 1、钩子是一种被声明在抽象类中的方法,当只有空的或者默认的实现。
* 2、钩子的存在,可以让子类有能力对算法的不同点进行挂钩(重写)。
* 3、要不要挂钩(重写),由子类自己决定。
*/
protected boolean wantAddCondiments() {
return true;
}
}
这里,我们定义了一个抽象模板,规范了具体的冲泡流程 —— 把水煮沸->泡饮料->倒杯子->添加调料。在抽象模板中,我们有三种类型的方法,一种需要子类提供实现的,我们定义为 abstract ;一种父类提供实现,又不希望被子类覆盖的,我们定义为 private;还有一种称为钩子方法,父类提供默认实现,子类可自由选择是否重写父类的方法。
2、具体模板
public class Coffee extends CaffeineBeverage {
@Override
protected void brew() {
System.out.println("Dripping Coffee through filter");
}
@Override
protected void addCondiments() {
System.out.println("Adding Suger and Milk");
}
}
public class Tea extends CaffeineBeverage {
@Override
protected void brew() {
System.out.println("Steeping the tea");
}
@Override
protected void addCondiments() {
System.out.println("Adding Lemon");
}
/**
* @Description 子类重写钩子方法
*/
@Override
protected boolean wantAddCondiments() {
return false;
}
}
具体模板继承了抽象模板,实现了抽象模板中定义为 abstract 的步骤方法,并可以自己选择是否重写钩子方法。
3、测试
public class Test {
public static void main(String[] args) {
CaffeineBeverage coffee = new Coffee();
CaffeineBeverage tea = new Tea();
coffee.prepareRecipe();
tea.prepareRecipe();
}
}
三、总结
- 模板方法模式的通用类图非常简单,仅仅使用了Java的继承机制,但它是一个非常广泛的模式。
- 优点:
1、封装不变部分,扩展可变部分。把认为不变部分的算法封装到父类中实现,而可变部分的则可以通过继承来继续扩展。可以将代码最大复用化。
2、父类规范算法行为,子类提供完整实现。 - 使用场景:
1、多个子类有共有的方法,并且逻辑基本相同。
2、一次性实现一个算法的不变部分,并且将可变的行为留给子类来完成。 - 为防止子类改变模板方法中的算法,可以将模板方法声明为 final。
- 策略模式和模板方法模式都封装算法,但是策略模式使用的是组合,模板方法模式使用的是继承。
- 工厂方法是模板方法的一种特殊版本。
- 模板方法导致一种反向的控制结构,这种结构有时被称为“好莱坞原则“。
- 好莱坞原则:别调用我们,我们会调用你。在好莱坞原则之下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我们,我们会调用你”。
- 低层组件可以调用高层组件中的方法(实际上子类会常常调用其从父类中继承所来的方法),但是我们要做的是要避免让高层和底层组件之间有明显的环状依赖。