概述:
我们最近在开发一个支持多种压缩类型文件的解压缩且制作成pdf的一个应用。对我们的架构来说我们需要支持多种压缩文件类型,但却有固定的操作顺序(先解压缩,在读取里面的文件分析、制作pdf)。我们抽取他们的共同点:这些操作的固定顺序,把他放到我们的父类里;他们的变化点:这些个具体的操作,去留给不同的子类去实现。这个就是模板方法模式,他定义一个操作中的算法的骨架(例子中的固定的操作顺序),而将一些步骤延迟到子类中(例子中的多种压缩文件的解压缩)。
Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。是一种比较简单的设计模式,但却是代码复用的一项基本技术,在类库中尤其重要。使用的也比较普遍。
类图与实例:
这里涉及到两个角色:
抽象模版(AbstractClass)角色:
定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。
定义并实现了一个模版方法。这个模版方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。这个就是定义了我们的固定的操作顺序。
具体模版(ConcreteClass)角色:
实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
每一个抽象模版角色都可以有任意多个具体模版角色与之对应,而每一个具体模版角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。就是对我们的多个压缩文件的不同的解压缩的支持。
实例:
#include <iostream>
template <typename T> class CaffeineBeverage //咖啡因饮料
{
public:
void PrepareRecipe() //咖啡因饮料冲泡法
{
BoilWater(); //把水煮沸
Brew(); //冲泡
PourInCup(); //把咖啡因饮料倒进杯子
AddCondiments(); //加调料
}
void BoilWater()
{std::cout << "把水煮沸" << std::endl;}
void Brew()
{static_cast<T *>(this)->Brew();}
void PourInCup()
{std::cout << "把咖啡倒进杯子" << std::endl;}
void AddCondiments()
{static_cast<T *>(this)->AddCondiments();}
};
class Coffee : public CaffeineBeverage<Coffee>
{
public:
void Brew()
{std::cout << "用沸水冲泡咖啡" << std::endl;}
void AddCondiments()
{std::cout << "加糖和牛奶" << std::endl;}
};
class Tea : public CaffeineBeverage<Tea>
{
public:
void Brew()
{std::cout << "用沸水浸泡茶叶" << std::endl;}
void AddCondiments()
{std::cout << "加柠檬" << std::endl;}
};
int main(void)
{
std::cout << "冲杯咖啡:" << std::endl;
Coffee c;
c.PrepareRecipe();
std::cout << std::endl;
std::cout << "冲杯茶:" << std::endl;
Tea t;
t.PrepareRecipe();
return 0;
}
适用性:
1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2.各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的“重分解以一般化”的一个很好的例子。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
3.控制子类扩展。模板方法只在特定点调用“Hook”操作,这样就只允许在这些点进行扩展。
实现要点:
1.Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
2.除了可以灵活应对子步骤的变化外,“不用调用我,让我来调用你”的反向控制结构是Template Method的典型应用。
3.在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法,纯虚方法),但一般推荐将它们设置为protected方法。 |