【设计模式】模板方法
一、前言
最近复习发现AQS使用了模板方法,自定义同步器时需要重写几个AQS提供的模板方法,Spring的DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions() 方法也使用了该设计模式,于是写篇文章加深理解。
模板方法模式的定义: 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
单看这句话可能还没搞懂这个设计模式是干嘛的,下面看一个例子。
二、示例
不用设计模式,冲咖啡和泡茶的两个类分别是下面这样的。
public class Coffee {
// 冲泡咖啡的算法
void prepareRecipe(){
boilWater();
// 用沸水冲泡咖啡
brewCoffeeGrinds();
pourInCup();
// 加牛奶和糖
addSugarAndMilk();
}
private void addSugarAndMilk() {
}
private void pourInCup() {
}
private void brewCoffeeGrinds() {
}
private void boilWater() {
}
}
public class Tea {
// 泡茶的算法
void prepareRecipe(){
boilWater();
// 用沸水浸泡茶叶
steepTeaBag();
pourInCup();
// 加柠檬
addLemon();
}
private void addLemon() {
}
private void pourInCup() {
}
private void steepTeaBag() {
}
private void boilWater() {
}
}
很容易就可以发现这两个类中有重复代码,boilWater()和pourInCup()都重复了可以提取出来,因为茶和饮料都是咖啡饮料可以定义一个超类CaffeineBeverage。
进一步分析,两种冲泡法其实都用了相同的算法:
- 把水煮沸
- 用热水泡咖啡或茶
- 把饮料倒进杯子
- 在饮料里加调料
所以prepareRecipe()也可以抽象成一个,如下:
final void prepareRecipe(){
boilWater();
// 交给对应的子类实现
brew();
pourInCup();
// 交给对应的子类实现
addCondiments();
}
最后,重构后的代码如下。
咖啡因饮料超类
public abstract class CaffeineBeverage {
/**
* 模板方法
* 定义的算法步骤
*/
final void prepareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
}
/**
* 添加调料
*/
protected abstract void addCondiments();
/**
* 冲泡
*/
protected abstract void brew();
/**
* 煮开水
*/
private void boilWater() {
System.out.println("Boiling water");
}
/**
* 把饮料倒进杯子
*/
private void pourInCup() {
System.out.println("Pouring in cup");
}
}
Tea
public class Tea extends CaffeineBeverage {
@Override
protected void addCondiments() {
System.out.println("Adding Lemon");
}
@Override
protected void brew() {
System.out.println("Steeping the tea");
}
}
Coffee
public class Coffee extends CaffeineBeverage {
@Override
protected void addCondiments() {
System.out.println("Adding Sugar and Milk ");
}
@Override
protected void brew() {
System.out.println("Dripping Coffee through filter");
}
}
代码的类图就变成了下面的样子。
道理我懂,可是使用了模板方法模式之后有什么好处吗?
原本的实现 | 模板方法后的实现 |
---|---|
Coffee和Tea之间存在重复代码 | CaffeineBeverage类实现了代码复用最大化 |
Coffee和Tea控制了算法 | 由CaffeineBeverage类主导一切,拥有并保护算法 |
对算法所做的代码改变,需要修改子类很多地方 | 算法只存在一个地方,很容易修改 |
算法的知识和它的实现分散在许多类中 | CaffeineBeverage类专注算法本身,而由子类提供完整的实现 |
三、总结
设计模式这么多种,要做到灵活运用还真是长路漫漫啊。