C++ 设计模式——模板方法模式
C++ 设计模式——模板方法模式
模板方法模式是一种行为设计模式,它定义了一个算法的骨架,而将一些步骤延迟到子类中。通过这种方式,模板方法允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。
引入“模板方法”设计模式的定义(实现意图):定义一个操作中的算法的骨架(稳定部分),而将一些步骤延迟到子类中去实现(父类中定义虚函数,让子类实现/重写这个虚函数)从而达到在整体稳定的情况下产生一些变化的目的。
1. 模板方法模式的主要组成部分
- 抽象类(Abstract Class):定义了一个模板方法,包含算法的骨架。它声明了一个或多个抽象步骤,供具体类实现。
- 具体类(Concrete Class):继承自抽象类,具体实现抽象类中声明的步骤。每个具体类提供自己的算法实现。
- 模板方法(Template Method):在抽象类中定义的公共方法,按照固定的顺序调用抽象步骤和具体步骤,确保算法的执行顺序。
- 具体步骤(Concrete Steps):可以在抽象类中实现的具体方法,提供算法中不变的部分,通常用于共享的实现。
- 客户端(Client):使用模板方法来执行算法,创建具体类的实例并调用模板方法,而不需要了解具体实现的细节。
2. 逐步重构并引入模板方法模式
假设我们需要处理不同格式的数据(如 CSV 和 JSON)。最初的实现可能是重复的代码。逐步重构的过程如下:
2.1 初始实现
为每种数据格式编写独立的处理逻辑:
#include <iostream>
#include <string>
// CSV 数据处理
void processCSV() {
std::cout << "Reading data from CSV file." << std::endl;
std::cout << "Processing CSV data." << std::endl;
std::cout << "Writing data to CSV file." << std::endl;
}
// JSON 数据处理
void processJSON() {
std::cout << "Reading data from JSON file." << std::endl;
std::cout << "Processing JSON data." << std::endl;
std::cout << "Writing data to JSON file." << std::endl;
}
int main() {
processCSV();
processJSON();
return 0;
}
2.2 提取共性并引入模板方法模式
识别出读取、处理和写入的共性步骤,并创建一个抽象类 DataProcessor
,定义模板方法 process()
和虚函数:
class DataProcessor {
public:
void process() {
readData();
processData();
writeData();
}
protected:
virtual void readData() = 0;
virtual void processData() = 0;
virtual void writeData() = 0;
};
2.3 实现具体类
为每种数据格式实现具体类,重写虚函数:
class CSVDataProcessor : public DataProcessor {
private:
void readData() override {
std::cout << "Reading data from CSV file." << std::endl;
}
void processData() override {
std::cout << "Processing CSV data." << std::endl;
}
void writeData() override {
std::cout << "Writing data to CSV file." << std::endl;
}
};
class JSONDataProcessor : public DataProcessor {
private:
void readData() override {
std::cout << "Reading data from JSON file." << std::endl;
}
void processData() override {
std::cout << "Processing JSON data." << std::endl;
}
void writeData() override {
std::cout << "Writing data to JSON file." << std::endl;
}
};
3. 模板方法模式的 UML 图
模板方法模式的 UML 类图通常包含一个抽象类和多个具体类。以下是模板方法模式的 UML 图示例:
UML 图详细介绍
-
类:
DataProcessor
是抽象类,定义了模板方法process()
和三个虚函数。CSVDataProcessor
和JSONDataProcessor
是具体类,分别实现了读取、处理和写入 CSV 和 JSON 数据的逻辑。
-
继承关系:
- 继承关系通过带实线箭头表示,显示了
CSVDataProcessor
和JSONDataProcessor
继承自DataProcessor
。
- 继承关系通过带实线箭头表示,显示了
-
方法可见性:
+
:表示公共方法,可以被外部访问,比如process()
。-
:表示私有方法,只能在类内部访问,比如readData()
、processData()
和writeData()
。#
:表示保护方法,子类可以访问,但外部无法访问。
4. 模板方法模式的优点
- 代码复用:通过将公共的算法步骤放在基类中,子类可以重用这些步骤,减少代码重复。
- 控制反转:基类控制算法的流程,子类只需实现特定的步骤,增强了代码的灵活性。
- 易于扩展:添加新算法或修改现有算法只需创建新的子类,不影响其他部分。
- 清晰的算法结构:将算法的结构和步骤明确分开,提升代码的可读性和可维护性。
5. 模板方法模式的缺点
- 类的数量增加:每个具体算法都需要一个子类,可能导致类的数量迅速增加,增加管理复杂性。
- 灵活性不足:由于算法的结构在基类中固定,子类无法改变算法的整体结构,只能修改特定步骤。
- 调试困难:当算法步骤较多时,调试可能变得复杂,尤其是在多个子类之间切换时。
- 不适合所有场景:在一些简单的场景中,使用模板方法模式可能显得过于复杂,增加了不必要的开销。
6. 模板方法模式适用场景
- 算法的框架:当你有一个算法的框架,但其中某些步骤需要在多个子类中实现时,模板方法模式非常适合。例如,处理不同格式的文件(如 CSV、XML、JSON)时,可以定义一个通用的处理流程。
- 代码复用:当多个类有相似的操作步骤,但具体实现不同,可以使用模板方法来复用代码,减少重复。
- 控制算法的执行顺序:当你需要控制算法中某些步骤的执行顺序,而这些步骤的实现可能在子类中不同。
个算法的框架,但其中某些步骤需要在多个子类中实现时,模板方法模式非常适合。例如,处理不同格式的文件(如 CSV、XML、JSON)时,可以定义一个通用的处理流程。 - 代码复用:当多个类有相似的操作步骤,但具体实现不同,可以使用模板方法来复用代码,减少重复。
- 控制算法的执行顺序:当你需要控制算法中某些步骤的执行顺序,而这些步骤的实现可能在子类中不同。
- 框架设计:适用于设计框架,允许用户在特定的步骤中扩展或自定义行为,而不改变整体结构。
完整代码示例
以下是完整的实现代码:
#include <iostream>
#include <string>
// 抽象类
class DataProcessor {
public:
void process() {
readData();
processData();
writeData();
}
protected:
virtual void readData() = 0; // 读取数据
virtual void processData() = 0; // 处理数据
virtual void writeData() = 0; // 写入数据
};
// 具体类:CSV 数据处理
class CSVDataProcessor : public DataProcessor {
private:
void readData() override {
std::cout << "Reading data from CSV file." << std::endl;
}
void processData() override {
std::cout << "Processing CSV data." << std::endl;
}
void writeData() override {
std::cout << "Writing data to CSV file." << std::endl;
}
};
// 具体类:JSON 数据处理
class JSONDataProcessor : public DataProcessor {
private:
void readData() override {
std::cout << "Reading data from JSON file." << std::endl;
}
void processData() override {
std::cout << "Processing JSON data." << std::endl;
}
void writeData() override {
std::cout << "Writing data to JSON file." << std::endl;
}
};
// 示例用法
int main() {
DataProcessor* processor;
// 处理 CSV 数据
processor = new CSVDataProcessor();
processor->process();
delete processor;
// 处理 JSON 数据
processor = new JSONDataProcessor();
processor->process();
delete processor;
return 0;
}