本篇介绍了模板方法模式及相关的面向对象设计原则。模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中实现。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
1 引例
例如有一个任务需要用到某个程序库,程序库开发人员设计了一个 Library 类,它实现了三种方法,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #ifndef LIBRARY_H #define LIBRARY_H class Library{ public: void Step1(){ } void Step3(){ } void Step5(){ } }; #endif
|
而应用程序开发人员要实现这个任务的话,还需要编写一些代码,并调用程序库,因此他可能会编写以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #ifndef APPLICATION_H #define APPLICATION_H class Application{ public: void Step2(){ } void Step4(){ } }; #endlif
|
最终实现整个任务流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include "Application.h" #include "Library.h" int main(int argc, char** argv) { Library lib(); Application app(); lib.Step1(); if(app.Step2()){ lib.Step3(); } for(int i = 0; i < 4; i++){ app.Step4(); } lib.Step5(); }
|
2 模板方法模式
上面的实现的问题在于,无论谁来使用程序库,完成类似的任务,都需要编写主函数中的一个完整流程,并编写自己的 Application 类,因此对于这类任务来说,流程是固定的,而库代码是不变的部分,Application 代码是变化的部分,因此我们可以设计一个方法模板,改模板按照固定流程实现某类任务,任务中有些步骤固定不变,但某些步骤可能发生改变,将这些可能发生变化的部分设定为纯虚函数,交给子类去实现,这样在客户代码中只需要调用统一的接口,就可以实现不同的任务了。
于是首先是模板方法接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #ifndef LIBRARY_H #define LIBRARY_H class Library{ public: void Run(){ Step1(); if(Step2()){ Step3(); } for(int i = 0; i < 4; i++){ Step4(); } Step5(); } virtual ~Library(){ }; protected: void Step1(){ } void Step3(){ } void Step5(){ } virtual bool Step2() = 0; virtual void Step4() = 0; }; #endif
|
然后让具体的程序继承该接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #ifndef APPLICATION_H #define APPLICATION_H #include"Library.h" class Application : public Library{ public: bool Step2(){ } void Step4(){ } }; #endif
|
这样一来,主程序就变得非常简单,并且h后续维护也不需要做任何更改:
1 2 3 4 5 6 7
| #include <iostream> #include "Application.h" int main(int argc, char** argv) { Library *pLib = new Application(); pLib->Run(); return 0; }
|
Template Method 模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性) 为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。