AkiraZheng's Time.

设计模式5:模板方法

Word count: 1.2kReading time: 5 min
2024/02/10

前言

本文中所有设计模式Github代码
其中本文模板方法相关代码在TemplateMethodPattern.h文件中

一、模板方法基础

1. 模板方法的提出

对于某项具体任务,模板方法模式常常拥有稳定的整体操作结构,但各个子步骤的具体实现可能各不相同(改变的)。那么如何在稳定的操作结构前提下,灵活应变各个子步骤的变化或者晚期实现需求?

2. 模板方法的设计思想&&代码实现(C++)

已知在程序开发中,框架开发人员(程序库开发人员)常常会提供一些稳定的操作结构和方法,而一些可变的具体的操作步骤则由应用开发人员来实现。这种情况下,框架开发人员可以使用模板方法模式来实现

2.1 模板方法使用场景示例

假设我们需要设定一系列步骤step1~step5来实现某项功能,其中step1、step3、step5的实现是稳定的,可以在程序库lib开发中实现

但是step2和step4的实现是不稳定的,需要在应用开发中实现

整体的step1~step5的步骤顺序和使用框架是稳定

2.2 未使用模板方法的代码实现思路

当我们没有采用模板方法模式时,我们会在程序库lib开发中提供step1、step3、step5稳定实现,然后在应用开发中提供step2和step4不稳定实现。最后在main程序中实现step1~step5的调用来实现具体功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 程序库开发人员
#include <iostream>
using namespace std;

class Library{
public:
void step1(){
cout << "Library::step1()" << endl;
}
void step3(){
cout << "Library::step3()" << endl;
}
void step5(){
cout << "Library::step5()" << endl;
}
};
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
// 应用程序开发人员
class Application
{
public:
bool step2(){
cout << "Application::step2()" << endl;
//...
return true;
}
void step4(){
cout << "Application::step4()" << endl;
}
};

int main(){
Library lib;
Application app;

lib.step1();

if(app.step2()){//step2的实现是不稳定的
lib.step3();
}

for(int i = 0; i < 4; i++){
app.step4();//step4的实现是不稳定的
}

lib.step5();

return 0;
}

这种方法属于早绑定的实现方式,在结构化的设计思维,也就是晚实现的应用程序人员开发程序去调用更早实现的lib库

这种实现方法是有缺点的:对于稳定的step1~step5的调用是分散的,不利于维护

2.3 使用模板方法的代码实现思路

解决上述问题的方法思路是:

稳定的main函数中的整体步骤实现也放在稳定的程序库lib中,然后在应用开发中实现不稳定的步骤

由于lib库中也需要用到app的实现,因此我们可以将两个不稳定的函数作为lib库的虚函数,交由应用程序开发人员继承实现

这样在main中直接创建一个lib库的多态指针,然后调用lib的整体实现步骤框架函数就可以

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
// 程序库开发人员
#include <iostream>
using namespace std;

class Library{
public:
void step1(){
cout << "Library::step1()" << endl;
}
void step3(){
cout << "Library::step3()" << endl;
}
void step5(){
cout << "Library::step5()" << endl;
}
void run(){
step1();
if(step2()){//支持变化 ==> 虚函数的多态调用
step3();
}
for(int i = 0; i < 4; i++){
step4();//支持变化 ==> 虚函数的多态调用
}
step5();
}
virtual bool step2() = 0;
virtual void step4() = 0;
virtual ~Library(){};
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 应用程序开发人员
class Application:public Library
{
public:
bool step2(){
cout << "Application::step2()" << endl;
//...
return true;
}
void step4(){
cout << "Application::step4()" << endl;
}
};

int main(){
Library* plib = new Application();
plib->run();
delete plib;
return 0;
}

这种方法属于晚绑定的实现方式,是面向对象开发的设计思维,也就是早实现的lib库通过开发稳定的算法骨架(Run函数),并将一些步骤延迟(延迟也就是虚函数)到晚实现的应用程序开发人员去实现

二、总结

  • 在面向对象的设计中,我们要将稳定的代码写成非虚函数,将不稳定的代码写成虚函数,这样可以实现稳定代码的复用不稳定代码的延迟实现

  • 设计模式的假设是必须有一个稳定点,那么当所有代码都是不稳定的时候,就不适合使用设计模式了

  • 设计模式最大的特点是在变化和稳定中间寻找隔离点,将变化的部分通过虚函数隔离出来,使得稳定的部分不受影响

  • 虚函数是面向对象里面最核心的晚绑定实现技术

CATALOG
  1. 前言
  2. 一、模板方法基础
    1. 1. 模板方法的提出
    2. 2. 模板方法的设计思想&&代码实现(C++)
      1. 2.1 模板方法使用场景示例
      2. 2.2 未使用模板方法的代码实现思路
      3. 2.3 使用模板方法的代码实现思路
  3. 二、总结