下面,我将用泡茶 和 泡咖啡的例子来描述模板设计模式的思想:
先来看看泡茶手册:
- 烧开水
- 把茶叶放入被子中
- 把开水倒入杯子中
- 喝茶
泡咖啡手册:
- 烧开水
- 把咖啡倒进杯子中
- 把开水倒进杯子中
- 喝咖啡
首先,如果不考虑什么设计模式,现在让我们来实现这两个过程,我们可以用一下代码实现:
运行结果:
运行结果:
但是这样写感觉有问题,这样的话有许多代码似乎是重复的,而且泡茶和泡咖啡的步骤很相似,所以,我们可以把他们放在一个父类里面,把相同的步骤在父类中实现,不同的步骤交给子类去实现 ---- 模板设计模式。
父类代码如下:其中泡茶和跑咖啡的步骤相同的部分放在父类中写好,不同的定义为抽象方法,交给子类去具体实现
茶类的实现:
运行结果:
咖啡类的实现:
运行结果:
费这么大劲,这么写有什么好处呢:
使用普通的方式 | 使用模板设计模式的方式 |
Coffee 或 Tea主导一切,控制算法 | 由父类主导一切,它拥有算法并且保护算法 |
Coffee 和 Tea 之间存在大量重复代码 |
有父类存在,将代码复用最大化 |
如果算法发生变化,需要在Coffee 和 Tea中做许多变化 | 算法只存在与一个地方,容易修改 |
代码弹性差,有新饮料,相似的饮料加入需要做许多工作 | 弹性高,有新的相似的饮料加入只需要实现自己对应的一些方法 |
最后,在利用上面的这个例子,完整的在写一遍模板设计模式的代码,同时引入一种方法,叫 “钩子” 方法,所谓的钩子方法,举个例子来说吧,就是比如有的人在喝咖啡的时候,会习惯加糖或牛奶,而有的习惯不加,所以在模板方法中引入钩子方法,以便于用户可以选择是否要加这些东西。代码如下:
父类代码:
public abstract class Drink {
public void boilingWater()
{
System.out.println("把水烧开...");
}
// 具体要添加的东西
public abstract void addXxx() ;
public void addWater()
{
System.out.println("加水...");
}
public abstract void drinkXxx() ;
public boolean ifAddSugarAndMilk()
{
return true; // 默认返回真,在子类中再覆写这个方法
}
public void addSugarAndMilk()
{
System.out.println("加糖和牛奶...");
}
public final void preparedWork()
{
boilingWater();
addXxx();
if (ifAddSugarAndMilk())
{
addSugarAndMilk();
}
addWater();
drinkXxx();
}
}
茶类:
public class Tea extends Drink {
@Override
public boolean ifAddSugarAndMilk() {
return false; // 茶中不要牛奶和糖
}
@Override
public void addXxx() {
System.out.println("添加茶...");
}
@Override
public void drinkXxx() {
System.out.println("喝茶...");
}
public static void main(String[] args) {
Drink tea = new Tea();
tea.preparedWork();
}
}
运行结果:
咖啡类:
import java.util.Scanner;
public class Coffee extends Drink {
@Override
public boolean ifAddSugarAndMilk() {
System.out.println("你是否想加糖和牛奶(Y/N):");
Scanner s = new Scanner(System.in);
String str = s.next();
if ("Y".equals(str)) {
return true;
}
return false;
}
@Override
public void addXxx() {
System.out.println("添加咖啡...");
}
@Override
public void drinkXxx() {
System.out.println("喝咖啡...");
}
public static void main(String[] args) {
Drink coffee = new Coffee();
coffee.preparedWork();
}
}
运行结果: