先用一个简单的模式来引入。
1. 须知
本系列的博文的设计模式类型不从创建型,结构型,行为型三个方向进行介绍,而是从封装变化角度对模式分类。具体分类不在这里说明,而是在适当的时候会进行总结,一并介绍。本系列源码总是在博文的最后的小节。
2. 情景与意图
在我们写论文或者写某个PPT时,通常老师都会给我们一个模板,而每一个同学都会根据模板来写,然后每个同学的论文或者PPT的内容肯定是不一样的。这个例子中呢,模板就是一个稳定的整体结构,而每个标题下的内容却又是变化的,最终的每一个论文或者PPT都是不一样的。
在我们的日常开发中,总有这样一个场景,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,比如lib与程序之间、framework与应用之间等等。
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变 化或者晚期实现需求?——模板方法模式。
3. 模板方法模式
定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤,其设计的原则符合开闭原则,对扩展时是开放的,但是对内修改是关闭的。
4. 理解模板方法
假设现在要实现一个游戏的框架,代码结构如下:
class DPGamesFramework {
protected:
virtual bool loadingGame();
virtual bool startGame();
virtual bool playingGame();
virtual bool endGame();
public:
virtual void Run() final;
virtual ~DPGamesFramework();
private:
void errorLog(std::string funcName);
};
上面顶一个了一个游戏框架的头文件,前面四个方法主要是交给子类去实现,因为不同的游戏,其内容肯定是不一样的。主要看看Run方法,Run方法带了final关键字,也就是说无法被子类重写,因为该方法必须是一个稳定的结构
,因此需要这样设计,现在看看里面的实现。
void DPGamesFramework::Run() {
if (!loadingGame()) {
errorLog("loadingGame");
return;
}
if (!startGame()) {
errorLog("startGame");
return;
}
if (!playingGame()) {
errorLog("playingGame");
return;
}
if (!endGame()) {
errorLog("endGame");
return;
}
}
Run方法定义了一个游戏的主要流程,然后让子类去实现变化
的步骤。
现在顶一个继承自这个游戏狂的吃鸡游戏:
class DPCjGame : public DPGamesFramework {
public:
bool loadingGame();
bool startGame();
bool playingGame();
bool endGame();
};
然后使用一下:
int main() {
DPGamesFramework* cjGame = new DPCjGame();
cjGame->Run();
return 0;
}
上面贴了部分代码,主要用于理解。从上面的代码可以看到模板方法模式的一个例子。用一个基类定义好重写的方法,最好加上override关键字,强制子类重写,这些要重写方法就是变化的。在定义稳定的部分,也就是run方法,这是最主要的部分。
从而实现了一个简单的模板方法,在日常开发中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现,此时就可以用到模板方法。
5. 总结
模板方法模式是非常常见且非常基础的一个重要的设计模式,用这个模式开头可以很快的体验到设计模式的奇妙。“不要调用我,让我来调用你”的反向控制结构是模板方法模式的典型应用,这其中还是前面说到,变化与稳定的博弈,封装变化,依赖抽象。
最后,整个模板方法的设计模式的代码见【模板方法模式C++源码】