文章目录
设计模式之模板方法—Template Method
0、模式类型
“组件协作”模式:
- 现代软件专业分工之后的第一个结果是“框架与应用程序的划 分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之 间的松耦合,是二者之间协作时常用的模式。
- 典型模式 :
- Template Method
- Observer / Event
- Strategy
1、Template Method
1.1、动机(Motivation)
- 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作 结构,但各个子步骤却有很多改变的需求,或者由于固有的原因 (比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
- 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?
1.2、应用
**定义:**定义一个操作中的算法的框架, 而将一些步骤延迟到子类中。 使得子类可以不改。
定义是什么意思?简单点理解就是在父类中将一个具体的流程通过函数调用封装到一个模板函数中,然后子类只需要重写流程中需要变化的方法。如果还没有理解,下面将通过一个实际案例进行示例:
需求定义:现在有一个开发任务,需要交给两个开发人员,一个是Library开发人员,另一个是你,进行Application方面的开发任务。Library开发上层的1、3、5步骤,这三个步骤是固定的,基本不需要变动。你需要开发下层的2、4步骤,这两个步骤的实现不是固定的,会发生变化。然后将五个步骤按照顺序进行执行(这个过程假定是相对稳定的)。
看到这个需求的第一实现思路是什么?一定是分别实现,然后通过main调用五个步骤的函数吧,比如下边的代码:
1.2.1、普通方式实现
Library开发人员:
//Library开发人员
#include <iostream>
using namespace std;
class Library{
public:
//稳定
void step1(){
cout << "我实现了步骤一" << endl;
}
//稳定
void step3(){
cout << "我实现了步骤三" << endl;
}
//稳定
void step5(){
cout << "我实现了步骤五" << endl;
}
};
Application开发人员:
//Application开发人员
#include<iostream>
using namespace std;
class Application{
public:
//变化
void step2()
{
cout << "我实现了步骤二" << endl;
}
//变化
void step4()
{
cout << "我实现了步骤四" << endl;
}
};
然后Application创建两个对应的对象调用整个流程调用整个流程:
int main() {
Application application;
Library library;
//整个流程是稳定的
library.step1();
application.step2();
library.step3();
application.step4();
library.step5();
return 0;
}
运行结果如下所示:
相信很多人第一时间想到的解决方案是以上方式,上面这种方式虽然实现了具体的需求,但是这种实现方式是结构化的设计流程,并不是通过面向对象软件设计程序实现的,这种方式也没有实现晚绑定。
下面给出Template Method的实现方式
1.2.2、Template Method实现
Library开发人员:
//
// Created by long on 2022/10/27.
//
#include <iostream>
using namespace std;
#ifndef GOF_LIBRARY_2_H
#define GOF_LIBRARY_2_H
class Library{
//不要把没有用的方法暴露出去,但是又要让子类可以对虚函数重写
protected:
//稳定
void step1()
{
cout << "我实现了步骤一" << endl;
}
//变化
virtual void step2() =0;
//稳定
void step3()
{
cout << "我实现了步骤三" << endl;
}
//变化
virtual void step4() = 0;
//稳定
void step5()
{
cout << "我实现了步骤五" << endl;
}
public:
//Template Method编写
void run()
{
cout << "我使用了模板方法" << endl;
step1();
step2();
step3();
step4();
step5();
}
//如果要把一个类当做基类,析构函数必须设置为虚函数
virtual ~Library(){}
};
#endif //GOF_LIBRARY_2_H
Application开发人员:(在模板方法中只需要重写要变化的窗口)
//
// Created by long on 2022/10/27.
//
#include "Library_2.h"
#ifndef GOF_APPLICATION_2_H
#define GOF_APPLICATION_2_H
class Application : public Library
{
public:
void step2()
{
cout << "我实现了步骤二" << endl;
}
void step4()
{
cout << "我实现了步骤四" << endl;
}
};
#endif //GOF_APPLICATION_2_H
调用模板方法:
//Template Method模板方法调用
int main() {
Library* application = new Application();
application->run();
return 0;
}
运行结果:
相信大家可以看出区别,这种方式是使用了面向对象的软件设计流程,利用了面向对象的虚函数和继承,Application开发人员只需要重写变化的部分,调用模板函数,就可以实现需求。同时这种方式也满足了晚绑定。
1.3、两种设计流程
1.3.1、结构化软件设计流程
- 这种方式对于应用程度开发人员来说开发任务会比较重。(结构化设计方法)
1.3.2、面向对象的软件设计流程
- 这种方式可以在一定程度上实现代码的复用。
1.4、早绑定和晚绑定
早绑定:通俗理解就是一个晚的东西调用一个早的东西(C语言的主流方式)。
晚绑定:通俗理解就是一个早的东西调用一个晚的东西(面向对象出现之后常用机制)。
1.5、模式定义
- 定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟 (变化)到子类中。Template Method使得子类可以==不改变 (复用)一个算法的结构即可重定义(override 重写)==该算法的 某些特定步骤。 ——《设计模式》GoF
1.5、结构图(Structure)
1.6、总结
-
Template Method模式是一种非常基础性的设计模式,在面向对 象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性) 为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本 实现结构。
-
除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用 你”的反向控制结构是Template Method的典型应用。
-
在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将 它们设置为protected方法。
以上就是Template Method模板方法的理解,温故而知新,下一节策略模式~~~
态性) 为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本 实现结构。
-
除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用 你”的反向控制结构是Template Method的典型应用。
-
在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将 它们设置为protected方法。
以上就是Template Method模板方法的理解,温故而知新,下一节策略模式~~~