Template Method
Template Method
引言
- 现代软件专业分工之后的第一个结果是“框架与应用程序的划分”。
- “组件协作”模式通过晚期绑定,来实现框架与应用程序的松耦合,是二者之间协作时常用的模式。
典型模式
- Template Method
- Observer / Event
- Strategy
framework和library会有区别,但是在本节学习过程中不过多去探究它们的区别,都暂且将之称之为框架。
在现在软件开发过程中,通常会感觉先拉好软件框架,然后再向框架的不同部分填充代码即可。
Template Method
动机
实际情况
在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但是某些子步骤却又很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
提出问题
那么,如何在确定稳定操作结构的前提下,灵活应对各个子步骤的变化或者晚期实现需求呢?
假设现在需要这样的代码
- 一项任务的程序实现有step1 ~ step5总共5个子步骤,且整体结构相当稳定
- step1、step3、step5在lib库中实现,并且这三个步骤是稳定的
- step2和step4是在应用程序中实现,并且会随着需求的变化而变化。
为了对比,首先看一下结构化软件设计流程代码
lib库开发程序
//程序库开发人员
class Library{
public:
void Step1(){
//...
}
void Step3(){
//...
}
void Step5(){
//...
}
};
app开发程序
//应用程序开发人员
class Application{
public:
bool Step2(){
//...
}
void 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();
}
很显然,在上述程序中,程序开发顺序如下
再看面向对象设计流程的软件开发代码
lib程序库开发
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;
};
app程序开发
//应用程序开发人员
class Application : public Library {
protected:
virtual bool Step2(){
//... 子类重写实现
}
virtual void Step4() {
//... 子类重写实现
}
};
int main()
{
Library* pLib=new Application();
lib->Run();
delete pLib;
}
}
在面向对象程序设计中,程序开发流程如图所示。
程序整体结构
程序的整体结构相当稳定,在结构化程序设计中整体结构在main()中由app开发人员进行构建;而在面向对象程序设计中,被集成布置在lib库中的Run()函数中,并被声明为public属性以供外部整体调用。
程序稳定部分
在结构化程序设计中,程序的稳定部分(step1、step3和step5)由lib库开发人员进行开发,并被声明为public属性,以供app开发人员在main()函数中的整体结构中进行调用;而在面向对象设计中,只有整体程序结构的Run()方法被声明为public,而step1、step3和step5则被声明为protected属性,符合面向对象设计原则:接口隔离原则中,使得暴露出去的接口应该小而完备。
程序变化部分
在结构化程序设计中,程序的变化部分(step2、step4)由app开发人员声明为单独的类,并实现该函数。而在面向对象程序设计中,变化部分则被声明为lib库类的纯虚成员函数,app开发人员则是继承了类库并重写了step2和step4函数。在进行调用时则是通过基类指针来进行的。
在两种程序实现逻辑中,重点关注分别是如何应对稳定整体结构中某些子步骤的变化的:
首先看下面的图:
在程序开发设计中,library和framework库的开发往往都是早于应用开发的。
仔细捋一下上述两种程序设计的调用方法:
在结构化程序设计中,是app应用去调用library程序中的方法,也就是晚定义的方法调用早出现的方法,称为早绑定;而在面向对象设计方法中,是Library库中方法调用app应用中的方法,也就是早出现的方法去调用晚定义的方法,称之为晚绑定。
经过上述例子,再来看GOF对于Template Method方法的定义:
定义一个操作中的算法的骨架(稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。
设计模式的意义就在于在程序的变化部分和稳定部分之间寻找隔离点,将两者分离开来,从而管理变化,将变化带来的影响隔离在局部范围内。所以,一段程序中,若完全是变化的,无法使用设计模式,而完全是稳定的,也是无法使用设计模式的。
应用设计模式重点在于设别变化与稳定
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)