模板方法模式
在模板方法模式(Template Method)中,定义一个算法的骨架,将具体内容延迟到子类去实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。属于行为性模式。
UML类图
代码示例
下面以冲咖啡和泡茶举例 传统写法:
public class Coffee {
/**
* 咖啡的步骤
*/
void prepareRecipe() {
boilWater();
brewCoffeeGrinds();
pourIncup();
addSugarAndMilk();
}
public void boilWater() {
System.out.println("烧水");
}
public void brewCoffeeGrinds() {
System.out.println("用沸水冲泡");
}
public void pourIncup() {
System.out.println("倒入杯子中");
}
public void addSugarAndMilk() {
System.out.println("加糖加牛奶");
}
}
public class Tea {
/**
* 泡茶水的步骤
*/
void prepareRecipe() {
boilWater();
steepTeaBag();
pourIncap();
addLemon();
}
public void boilWater() {
System.out.println("烧水");
}
public void steepTeaBag() {
System.out.println("用沸水浸泡茶叶");
}
public void pourIncap() {
System.out.println("倒入杯子中~");
}
public void addLemon() {
System.out.println("加柠檬");
}
}
上面两个步骤是极其相似的,造成了代码重复。所以我们可以重构代码将部分代码放入基类中。
重构:
public abstract class Base{
public void boilWater() {
System.out.println("烧水");
}
public void pourIncap() {
System.out.println("倒入杯子中~");
}
}
class Tea extends Base{
//...
}
class Coffee extends Base{
//...
}
这是基本的解决方法,现在看我们代码的重复性降低了。其实泡茶和冲咖啡是很相似的操作,加柠檬和加糖,牛奶也是很相似的操作,可以考虑下是否也将其抽出到子类中呢?
再次重构:
public abstract class TemplateMethod {
/**
* 模板方法
*/
final void prepareRecipe() {
boilWater();
brew();
pourIncup();
addCondiments();
}
/**
* 冲泡
*/
abstract void brew();
/**
* 增加调料
*/
abstract void addCondiments();
void boilWater() {
System.out.println("烧开水");
}
void pourIncup() {
System.out.println("倒入杯中");
}
}
public class Coffee extends TemplateMethod {
@Override
void brew() {
System.out.println("冲泡咖啡");
}
@Override
void addCondiments() {
System.out.println("加糖和牛奶");
}
}
public class Tea extends TemplateMethod {
@Override
void brew() {
System.out.println("泡茶");
}
@Override
void addCondiments() {
System.out.println("加柠檬");
}
}
其实这就是***模板方法***模式。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
思考:倘若客户不想加糖加牛奶加柠檬呢(加调料)?这样就需要addCondiments()方法不执行,如何实现?
public abstract class TemplateMethod {
/**
* 模板方法
*/
final void prepareRecipe() {
boilWater();
brew();
pourIncup();
if (customerWantsCondiments()) {
addCondiments();
}
}
/**
* 冲泡
*/
abstract void brew();
/**
* 增加调料
*/
abstract void addCondiments();
void boilWater() {
System.out.println("烧开水");
}
void pourIncup() {
System.out.println("倒入杯中");
}
/**
* 钩子方法 可以用来控制算法执行流程
* 基类可以提供默认实现,由子类决定是否重写
*
* @return
*/
boolean customerWantsCondiments() {
return true;
}
}
/**
* 子类需要自己去实现钩子方法
*/
public class CoffeeWithHook extends TemplateMethod {
@Override
void brew() {
System.out.println("冲泡咖啡");
}
@Override
void addCondiments() {
System.out.println("加糖和牛奶");
}
@Override
boolean customerWantsCondiments() {
//子类重写钩子方法,决定是否需要控制算法的步骤
return false;
}
}
public class Main {
public static void main(String[] args){
TemplateMethod tea = new Tea();
tea.prepareRecipe();
System.out.println("=============");
TemplateMethod caffee = new Coffee();
caffee.prepareRecipe();
System.out.println("=============");
TemplateMethod hook = new CoffeeWithHook();
hook.prepareRecipe();
}
}
测试结果:
烧开水
泡茶
倒入杯中
加柠檬
=============
烧开水
冲泡咖啡
倒入杯中
加糖和牛奶
=============
烧开水
冲泡咖啡
倒入杯中
结论:通过子类重写钩子方法,达到了控制算法执行流程的目的。