模板方法模式的核心是“骨架与细节分离”,它通过基类定义算法的固定流程,并将可变步骤推迟到子类实现。其关键在于使用抽象基类定义算法骨架,其中模板方法通常是非虚函数以防止被重写,而可变步骤则通过纯虚函数(强制子类实现)和虚函数(带默认实现,子类可选覆盖)来实现,同时非虚函数用于所有子类共享的固定步骤。此外,还引入钩子函数(hook method),为子类提供扩展点而不强制实现。例如在文档处理示例中,processdocument() 是模板方法,固定了“加载→解析→格式化→保存”的流程;loaddocument() 和 savedocument() 是非虚函数,表示通用步骤;parsecontent() 是纯虚函数,必须由子类实现;applyformatting() 是虚函数,带有默认实现;shouldlogprogress() 是钩子函数,允许子类选择性地启用某些行为。

C++模板方法模式的核心在于它允许你定义一个算法的骨架,但将其中一些步骤的实现推迟到子类。说白了,就是把一个大任务的固定流程定死,但把其中一些个性化的、可变的部分留给不同的实现者去完成。

要设计C++中的模板方法模式,你需要一个抽象基类来承载这个算法的骨架。这个基类会包含一个非虚的(或有时是虚的,但通常不推荐覆盖整个模板)“模板方法”,它按特定顺序调用一系列“基本操作”。这些基本操作有些是纯虚函数,强制子类去实现;有些是虚函数,带有默认实现,子类可以选择性地覆盖;还有些是具体的非虚函数,代表所有子类都共享的固定步骤。
例如,想象一个处理文档的流程:
立即学习“C++免费学习笔记(深入)”;

#include <iostream>
#include <string>
#include <vector>
// 抽象基类:文档处理器
class DocumentProcessor {
public:
// 模板方法:定义处理文档的固定算法骨架
// 通常是非虚的,确保算法流程不被子类改变
void processDocument() {
std::cout << "--- 开始处理文档 ---" << std::endl;
// 固定步骤1:加载文档
loadDocument();
// 可变步骤1:解析内容(强制子类实现)
parseContent();
// 可变步骤2:应用格式(子类可选实现或使用默认)
applyFormatting();
// 固定步骤2:保存文档
saveDocument();
std::cout << "--- 文档处理完成 ---" << std::endl;
}
protected:
// 基本操作1:加载文档 (固定步骤)
void loadDocument() {
std::cout << "通用操作: 正在加载文档..." << std::endl;
// 实际加载逻辑...
}
// 基本操作2:解析内容 (纯虚函数,强制子类实现)
virtual void parseContent() = 0;
// 基本操作3:应用格式 (虚函数,带默认实现,子类可覆盖)
virtual void applyFormatting() {
std::cout << "通用操作: 正在应用默认格式..." << std::endl;
}
// 基本操作4:保存文档 (固定步骤)
void saveDocument() {
std::cout << "通用操作: 正在保存文档..." << std::endl;
// 实际保存逻辑...
}
// 钩子函数 (Hook Method): 子类可以重写以在特定点插入代码,但不强制
virtual bool shouldLogProgress() const {
return false; // 默认不记录进度
}
};
// 具体子类:PDF文档处理器
class PdfDocumentProcessor : public DocumentProcessor {
protected:
void parseContent() override {
std::cout << "PDF处理器: 正在解析PDF特定内容..." << std::endl;
// PDF解析逻辑...
}
// 覆盖默认格式化,应用PDF特定格式
void applyFormatting() override {
std::cout << "PDF处理器: 正在应用PDF排版格式..." << std::endl;
}
bool shouldLogProgress() const override {
return true; // PDF处理器选择记录进度
}
};
// 具体子类:文本文档处理器
class TextDocumentProcessor : public DocumentProcessor {
protected:
void parseContent() override {
std::cout << "文本处理器: 正在解析纯文本内容..." << std::endl;
// 文本解析逻辑...
}
// 不覆盖applyFormatting(), 使用基类的默认实现
};
/*
// 示例用法
int main() {
PdfDocumentProcessor pdfProcessor;
pdfProcessor.processDocument();
std::cout << "\n";
TextDocumentProcessor textProcessor;
textProcessor.processDocument();
return 0;
}
*/在这个例子里,processDocument() 就是模板方法,它固定了文档处理的流程:加载 -> 解析 -> 格式化 -> 保存。loadDocument() 和 saveDocument() 是所有子类都共享的固定步骤。parseContent() 是纯虚函数,强制子类必须提供自己的解析逻辑。而 applyFormatting() 是一个虚函数,提供了默认实现,子类可以选择是否覆盖。shouldLogProgress() 则是一个典型的钩子函数,子类可以决定是否启用某个可选行为。
模板方法模式的核心,我觉得,就是“骨架与细节分离”。它提供了一种机制,让你在基类中定义一个算法的步骤序列——也就是那个“骨架”——而将其中某些具体步骤的实现推迟到子类。这就像是定好了菜谱的主流程(比如先切菜,再炒,最后调味),但具体切什么菜、怎么炒、用什么调料,则由不同的厨师(子类)来发挥。

它特别强调一个原则,有时候被称为“好莱坞原则”:“别打电话给我们,我们会打电话给你。”这意味着,基类(算法的骨架)掌握着控制权,它在适当的时候会“调用”子类提供的具体实现。这保证了算法的整体结构稳定不变,同时又允许局部细节的灵活变化。这和策略模式有点不同,策略模式是客户端选择一个完整的算法,而模板方法模式是基类定义了一个算法的结构,然后让子类去填充其中的某些部分。可以说,模板方法模式更侧重于算法的“内聚性”和“流程控制”。
在C++里实现模板方法模式,主要依赖于面向对象的三大支柱之一:继承。
关键点包括:
= 0): 用于那些必须由子类提供的具体实现。如果一个基类包含纯虚函数,它就变成了抽象类,不能直接实例化,只能通过其具体子类来使用。这强制了子类必须实现这些步骤。以上就是C++模板方法模式如何设计 固定算法骨架与可变步骤实现的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号