模板方法的实现要素:抽象基类和具体子类。
抽象基类:(1)基本方法:共有的方法,用private修饰
(2)抽象方法:只知道原则,不知道具体实现,要延迟到子类中实现,用protected abstract修饰
(3)可选的钩子:具体的子类可以自行决定是否挂钩以及如何挂钩,从而影响算法的实现,用protected修饰
(4)Template方法:封装了所有子类共同遵循的算法框架,用final修饰,不让子类覆盖重写。
模板方法定义成final,即表示子类可以替换掉父类中的可变逻辑,但不能改变掉整体逻辑结构。(好莱坞原则)
具体子类:(1)实现基类中的抽象方法
(2)可选的覆盖钩子方法,来更加个性化来影响算法的局部行为。
总结:
准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模板方法。
模板方法模式的适用场景:
(1)算法或操作遵循相似的逻辑
(2)重构时(把相同的代码抽取到父类中)
(3)重要,复杂的算法,核心算法设计为模板算法
模板方法模式的优点:
(1)封装性好
(2)复用性好
(3)屏蔽细节
(4)便于维护
模板方法模式的缺点:
(1)继承(Java语言只支持单继承),但我们重构一个系统时,假如有些类已经继承了相关父类,那就无法再继承模板方法的抽象基类。
简单代码示例如下:
/**
* 抽象基类,为所有子类提供一个算法框架
*
* 提神饮料
*
* @author zhx
*
*/
public abstract class RefreshBeverage {
/*
* 制备饮料的模版方法 封装了所有子类共同遵循的算法框架
*/
public final void prepareBeverageTemplate() {
// 步骤1:将水煮沸
boilWater();
// 步骤2:泡制饮料
brew();
// 步骤3:将饮料倒入杯中
pourInCup();
if(isCustomerWantsCondiments()) {
// 步骤4:加入调味料
addCondiments();
}
}
/*
* Hook,钩子函数,提供一个默认或空的实现
* 具体的子类可以自行决定是否挂钩以及如何挂钩
* 询问用户是否加入调料
*/
protected boolean isCustomerWantsCondiments() {
return true;
}
/*
* 基本方法,将水煮沸
*/
private void boilWater() {
System.out.println("将水煮沸");
}
/*
* 抽象的基本方法,泡制饮料
*/
protected abstract void brew();
/*
* 基本方法,将饮料倒入杯中
*/
private void pourInCup() {
System.out.println("将饮料倒入杯中");
}
/*
* 抽象的基本方法:加入调味料
*/
protected abstract void addCondiments();
}
/**
* 具体子类,提供了制备咖啡的具体实现
* @author zhx
*
*/
public class Coffee extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("用沸水冲泡咖啡");
}
@Override
protected void addCondiments() {
System.out.println("加入糖和牛奶");
}
}
/**
* 具体子类:提供了制备茶的具体实现
* @author zhx
*
*/
public class Tea extends RefreshBeverage{
@Override
protected void brew() {
System.out.println("用80℃的热水浸泡茶叶5分钟");
}
@Override
protected void addCondiments() {
System.out.println("加入柠檬");
}
/*
* 子类通过覆盖的形式选择挂载钩子函数
* @see edu.bnuz.zhx.design_pattern.template.RefreshBeverage#isCustomerWantsCondiments()
*/
@Override
protected boolean isCustomerWantsCondiments() {
return false;
}
}
public class RefreshBeverageTest {
public static void main(String[] args) {
System.out.println("制备咖啡中...");
RefreshBeverage coffee = new Coffee();
coffee.prepareBeverageTemplate();
System.out.println("咖啡制备完成...");
System.out.println("-----------------------------------");
System.out.println("制备茶中...");
RefreshBeverage tea = new Tea();
tea.prepareBeverageTemplate();
System.out.println("茶制备完成...");
}
}