文章目录
一、模板模式简介
Template Method模式也叫模板方法模式,是行为模式之一,它把具有特定步骤算法中的某些必要的处理委让给抽象方法,通过子类继承对抽象方法的不同实现改变整个算法的行为。
二、模板模式的结构
三、模板模式的角色与职责
- AbstractClass:抽象类的父类
- ConcreteClass:具体的实现子类
- templateMethod():模板方法
- method1()与method2():具体步骤方法
四、模板模式的具体实现
星巴兹咖啡现在有两种饮料,一种是茶,另一种是咖啡,让我们来看看他们的制作过程吧。
1、不使用模板模式
方案设计
类设计
我们先来看茶类:
// An highlighted block
package design.Template.gys.no;
public class Tea {
public void prepareRecipe() {
// TODO Auto-generated method stub
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
public void boilWater() {
// TODO Auto-generated method stub
System.out.println("Boiling water");
}
public void steepTeaBag() {
// TODO Auto-generated method stub
System.out.println("Steeping the tea");
}
public void pourInCup() {
// TODO Auto-generated method stub
System.out.println("Pouring into cup");
}
public void addLemon() {
// TODO Auto-generated method stub
System.out.println("Adding Lemon");
}
}
再看咖啡的制作方法:
// An highlighted block
package design.Template.gys.no;
public class Coffee {
public void prepareRecipe() {
// TODO Auto-generated method stub
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugerAndMilk();
}
public void boilWater() {
// TODO Auto-generated method stub
System.out.println("Boiling water");
}
public void brewCoffeeGrinds() {
// TODO Auto-generated method stub
System.out.println("Ddipping Coffee through filter");
}
public void pourInCup() {
// TODO Auto-generated method stub
System.out.println("Pouring into cup");
}
public void addSugerAndMilk() {
// TODO Auto-generated method stub
System.out.println("Adding suger and Milk");
}
}
非常好,大家都学会了星巴兹咖啡的都有技巧,可以回家自己制作了。让我们看看学习成果:
// An highlighted block
package design.Template.gys.no;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Tea t=new Tea();
Coffee c=new Coffee();
t.prepareRecipe();
System.out.println("---------------");
c.prepareRecipe();
}
}
// An highlighted block
Boiling water
Steeping the tea
Pouring into cup
Adding Lemon
---------------
Boiling water
Ddipping Coffee through filter
Pouring into cup
Adding suger and Milk
我们看到,两个制作方法中含大量重复的代码,Coffee和Tea两个类主导控制了算法,当我们需要改变时,需要修改较多的代码,并且类的阻止不具有弹性,算法的知识和实现分散。
现在要将这些代码抽出并抽象到基类中。
2、使用模板模式
方案设计
类设计
让我们看一下抽象之后的操作步骤吧。
在基类中,我们定义了两个抽象的方法,子类在继承的时候会选择覆盖,用以实现自己的方式。
// An highlighted block
package design.Template.gys.Template;
public abstract class Drink {
public void prepareRecipe() {
// TODO Auto-generated method stub
boilWater();
brew();
pourInCup();
addElement();
}
public void boilWater() {
// TODO Auto-generated method stub
System.out.println("Boiling water");
}
public abstract void brew();
public void pourInCup() {
// TODO Auto-generated method stub
System.out.println("Pouring into cup");
}
public abstract void addElement();
}
我们来看重新设计之后的饮料类。
茶:
// An highlighted block
package design.Template.gys.Template;
public class Tea extends Drink{
@Override
public void brew() {
// TODO Auto-generated method stub
System.out.println("Steeping the tea");
}
@Override
public void addElement() {
// TODO Auto-generated method stub
System.out.println("Adding Lemon");
}
}
咖啡:
// An highlighted block
package design.Template.gys.Template;
public class Coffee extends Drink{
@Override
public void brew() {
// TODO Auto-generated method stub
System.out.println("Ddipping Coffee through filter");
}
@Override
public void addElement() {
// TODO Auto-generated method stub
System.out.println("Adding suger and Milk");
}
}
可以看到,二者在继承的同时实现了自己的两个方法。Drink类知道了一切算法,并且只存在一个地方,修改较容易。Drink提供了一个冲泡算法的框架,让子类饮料实现自己的方法。
框架专注在算法本身,二子类提供完整的实现。
下面我们看一下测试代码:
// An highlighted block
package design.Template.gys.Template;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Drink t=new Tea();
Drink c=new Coffee();
t.prepareRecipe();
System.out.println("-------------");
c.prepareRecipe();
}
}
结果如下:
// An highlighted block
Boiling water
Steeping the tea
Pouring into cup
Adding Lemon
-------------
Boiling water
Ddipping Coffee through filter
Pouring into cup
Adding suger and Milk
可以说很完美了。
接下来我们看模板模式中的另一个工具。
3、钩子
类设计
钩子时在抽象类中的方法,但是只有空的或者默认的实现,钩子的存在让子类有能力对算法的不同点进行挂钩,要不要挂钩看子类。
我们使用钩子对是否加调料进行控制。
同样的先定义抽象类:
// An highlighted block
package design.Template.gys.hook;
public abstract class Drink {
public void prepareRecipe() {
// TODO Auto-generated method stub
boilWater();
brew();
pourInCup();
if(customer())
addElement();
}
public void boilWater() {
// TODO Auto-generated method stub
System.out.println("Boiling water");
}
public abstract void brew();
public void pourInCup() {
// TODO Auto-generated method stub
System.out.println("Pouring into cup");
}
public abstract void addElement();
public boolean customer() {
return true;
}
}
可以看到,抽象类中默认的钩子返回的是true,也就是需要添加调料,现在星巴兹咖啡只在咖啡中添加糖和牛奶,而不在茶中添加柠檬。所以咖啡类中不重新实现钩子,使用默认的实现,但是在茶中我们重写基类的钩子方法,使其不添加柠檬。
咖啡:
// An highlighted block
package design.Template.gys.hook;
public class Coffee extends Drink{
@Override
public void brew() {
// TODO Auto-generated method stub
System.out.println("Ddipping Coffee through filter");
}
@Override
public void addElement() {
// TODO Auto-generated method stub
System.out.println("Adding suger and Milk");
}
}
茶:
// An highlighted block
package design.Template.gys.hook;
public class Tea extends Drink{
@Override
public void brew() {
// TODO Auto-generated method stub
System.out.println("Steeping the tea");
}
@Override
public void addElement() {
// TODO Auto-generated method stub
System.out.println("Adding Lemon");
}
public boolean customer() {
return false;
}
}
跳过我们的测试过程,直接看结果:
// An highlighted block
Boiling water
Steeping the tea
Pouring into cup
-------------
Boiling water
Ddipping Coffee through filter
Pouring into cup
Adding suger and Milk
可以看到我们控制了抽象类中的部分算法的实现。也就是说,如果该算法的这个部分是可选的,那么我们就是用钩子来实现。
五、模板模式与策略模式的区别
- 侧重点不同:模板模式封装的是一个算法的具体步骤,策略模式通过接口将某一方法功能变为一个方法族
- 模板模式使用的是继承的方式实现,策略模式使用的是组合的方式
好莱坞原则:别调用我们,我们调用你。可以很好的防止依赖腐败。