- 参考书籍: 《Design Patterns: Elements of Reusable Object-Oriented Software》
模板方法模式和工厂方法模式分别隶属于行为模式(Behavioral Pattern)和创建类模式(创建型模式), 但是他们的核心思想却十分相似, 而且通常会同时出现。
模板方法模式
- 设计动机
- 将一个操作整体步骤定义好,把其中的一部分具体步骤延迟到子类去实现。模板方法可以让子类在不改变算法整体结构的情况下, 重新定义其中的特定步骤。
- 举例: 假设现在要编写一个支持屠宰家畜的框架, 家畜的类型包括牛羊等, 于是希望先定义一个屠宰场类 KillHouse, 该屠宰场会在调用KillAnimal(Animal animal) 方法时会接受运行时传入的参数(Sheep、Chicken ) 。现在假设, 宰杀家畜这个行为的整体操作流程是基本固定的,比如都是首先要判断传入的对象是不是可宰杀的牲畜, 如果可宰杀, 则先做一些准备工作customPrepareWork(),需要的准备工作可能会因为牲畜类型不同而不同, 然后调用一把刀(Knife)的chop( animal)方法, 宰杀完毕以后, 事后再进行一些通用的清理现场的工作 。
- 在以上的描述中 , 假设了宰杀牲畜前的准备工作因动物而异, 宰杀后的清理工作对于不同类型的牲畜都是通用的, 所以这一部分是可以被复用的代码, 可以放到父类中实现。 然后由子类去重写准备工作的方法customPrepareWork()。
abstract public class KillHouse {
private Knife knife = new DefaultKnife();
public void killAnimal(Animal animal)
{
if(animal.canBeKilled)
{
// 有一些因动物类别而不同的准备步骤需要
customPrepareWork();
knife.chop(animal); // 宰杀动物的操作
// 有一些通用的清理步骤
commonCleanWork();
}
else{
// 报警: 遭遇了不可宰杀的动物
}
}
protected abstract void customPrepareWork();
private void commonCleanWork() {
//通用的清理步骤
}
}
public class KillCowHouse extends KillHouse {
@Override
protected void customPrepareWork() {
// 重写父类的方法, 实现杀牛之前独特的准备工作
}
}
通过以上的代码可以发现, 父类固定了宰杀家禽killAnimal这项操作的整体结构或整体步骤, 实现了通用的清理工作, 子类KillCowHouse 重写了需要根据动物类型定制的准备工作。 killAnimal( Animal animal) 这个方法就是一个模板方法
模板方法模式与工厂方法模式之间的情侣关系
注意到之前编写的代码样例中, 用到了Knife来宰杀动物, 这个对象的获得是通过在父类里直接new DefaultKnife 完成的, 这样的话, 其实所有的动物,不论是牛还是鸡都在用同一把刀宰杀, 现假设杀鸡不能用牛刀的, 且想把刀的实例化工作,放到通用的准备工作中去, 则就会应用到工厂方法模式, 变成如下的样子。
abstract public class KillHouse {
private Knife knife = null;
public void killAnimal(Animal animal)
{
if(animal.canBeKilled)
{
// 有一些因动物类别而不同的准备步骤需要
customPrepareWork();
knife = createKnife();
if(knife != null)
{
knife.chop(animal); // 宰杀动物的操作
// 有一些通用的清理步骤
commonCleanWork();
}else{
// 报警: 没有成功获取到刀。
}
}
else{
// 报警: 遭遇了不可宰杀的动物
}
}
protected abstract void customPrepareWork();
protected abstract Knife createKnife(); // 新增的工厂方法
private void commonCleanWork() {
//通用的清理步骤
}
}
public class KillCowHouse extends KillHouse {
@Override
protected Knife createKnife() {
return new CowKnife();
}
@Override
protected void customPrepareWork() {
// 重写父类的方法, 实现杀牛之前独特的准备工作
}
}
通过以上的例子可以发现, 由于子类可能需要使用的实例不同, 所以增加了一个工厂方法, 把刀的实例化操作留给了子类去重写。 在这个过程中, 模板方法 killAnimal(Animal animal) 中调用了 createKnife() 这个工厂方法。
- 总结:
- 模板方式和工厂模式的核心思想非常类似, 都是把一些操作留给子类去实现。
- 模板方法中常常会调用工厂方法的, 他们之间存在着的紧密的情侣关系。
- 工厂方法模式和模板方法模式的区别在于:
- 模板方法模式的意义在于固定了一个算法的整体结构, 复用了其中通用的步骤, 将需要定制的部分留给了子类实现
- 工厂方法模式的意义在于解决了父类没有办法预知应该实现什么子类的问题