我们打算定义一个咖啡因饮料冲泡流程,把流程中相同的步骤放在一起,同时,不同的饮料还能有自己的具体实现。
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;还有一种称为钩子方法,父类提供默认实现,子类可自由选择是否重写父类的方法。
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 的步骤方法,并可以自己选择是否重写钩子方法。
public class Test {
public static void main(String[] args) {
CaffeineBeverage coffee = new Coffee();
CaffeineBeverage tea = new Tea();
coffee.prepareRecipe();
tea.prepareRecipe();
}
}