>> 什么时候、什么地点应用设计模式,比理解设计结构本身更为重要;
>> 设计模式的应用不宜先入为主,没有一步到位的设计模式,敏捷开发中提倡“Reafactoring to Patterns(重构到模式)”;
一. 动机
>> 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现;
>> 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或晚期实现需求?
二. 样例
程序库开发人员和应用程序开发人员分别开发某些功能,版本一:
// 程序库开发人员
class Library{
public:
void Step1(){ // ... }
void Step3(){ // ... }
void Step5(){ // ... }
};
// 应用程序开发人员
class Application{
public:
bool Step2(){ // ... }
bool Step4(){ // ... }
};
int main()
{
Library lib();
Application app();
lib.Step1();
if(app.Step2()){
lib.Step3();
}
for(int i = 0; i < 4; i++){
app.Step4();
}
lib.Step5();
}
版本二:
// 程序库开发人员
class Library{
public:
virtual ~Library(){}
// 稳定 template method
void Run(){
Step1();
if(Step2()){ // 支持变化 ==> 虚函数的多态调用
Step3();
}
for(int i = 0; i < 4; i++){
Step4(); // 支持变化 ==> 虚函数的多态调用
}
Step5();
}
protected:
void Step1(){ // ... } // 稳定
void Step3(){ // ... } // 稳定
void Step5(){ // ... } // 稳定
virtual bool Step2() = 0; // 变化
virtual bool Step4() = 0; // 变化
};
// 应用程序开发人员
class Application : public Library{
protected:
virtual bool Step2(){ // ... 子类重写实现}
virtual bool Step4(){ // ... 子类重写实现}
};
int main()
{
Library* pLib = new Application ();
pLib->Run();
delete pLib;
}
将两个版本以图示方法展示,版本一如下:
版本二如下:
版本一的调用关系是Application调用Library,这是一种早绑定的写法(因为Library一定是先于Application出现的,一个晚出现的调用一个早出现的,就是早绑定,这是c语言中主流的做法);而版本二中,通过虚函数,实现了Library调用Application,从而达到了晚绑定的目的(这是面向对象语言的特征)。
三. 要点总结
>> Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性),为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构;
>> 除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用;
>> 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设为protected方法;