1. 模式定义与核心思想
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。
核心思想:
-
代码复用:将不变的行为移到父类,去除子类中的重复代码。
-
控制反转:父类控制整体流程,子类实现具体细节(好莱坞原则:"Don't call us, we'll call you")。
-
框架设计:提供一种扩展点,让子类可以扩展或重新定义算法的特定部分。
2. 模式结构(角色分析)
模板方法模式包含两个主要角色:
-
AbstractClass (抽象类)
-
定义抽象的基本操作(Primitive Operations),这些操作由子类实现。
-
实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用基本操作,也调用定义在AbstractClass中的其他操作。
-
-
ConcreteClass (具体类)
-
实现父类所定义的一个或多个抽象基本操作。
-
每个ConcreteClass为算法提供不同的实现,但都遵循模板方法定义的总体结构。
-
关键方法:
-
模板方法 (Template Method) :定义算法的骨架,通常声明为
final
以防止子类重写。 -
基本操作 (Primitive Operations):抽象方法或钩子方法,由子类实现。
3. 经典C++实现示例
示例 1:饮料制作过程(经典示例)
展示咖啡和茶的制作过程,它们有相似的步骤但某些步骤的实现不同。
cpp
#include <iostream>
#include <memory>
// AbstractClass: 饮料制作
class Beverage {
public:
virtual ~Beverage() = default;
// 模板方法 - 定义算法骨架 (声明为final防止子类重写)
void prepareRecipe() final {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
serve();
}
protected:
// 基本操作 - 由子类实现
virtual void brew() = 0;
virtual void addCondiments() = 0;
// 具体操作 - 已有默认实现
void boilWater() {
std::cout << "Boiling water\n";
}
void pourInCup() {
std::cout << "Pouring into cup\n";
}
void serve() {
std::cout << "Serving beverage\n";
}
// 钩子方法 - 子类可以选择性重写
virtual bool customerWantsCondiments() {
return true; // 默认添加调料
}
};
// ConcreteClass: 咖啡
class Coffee : public Beverage {
protected:
void brew() override {
std::cout << "Dripping coffee through filter\n";
}
void addCondiments() override {
std::cout << "Adding sugar and milk\n";
}
// 重写钩子方法
bool customerWantsCondiments() override {
// 在实际应用中,这里可以询问用户
std::cout << "Would you like milk and sugar with your coffee? (y/n): ";
char response;
std::cin >> response;
return response == 'y' || response == 'Y';
}
};
// ConcreteClass: 茶
class Tea : public Beverage {
protected:
void brew() override {
std::cout << "Steeping the tea\n";
}
void addCondiments() override {
std::cout << "Adding lemon\n";
}
// 重写钩子方法
bool customerWantsCondiments() override {
std::cout << "Would you like lemon with your tea? (y/n): ";
char response;
std::cin >> response;
return response == 'y' || response == 'Y';
}
};
// 客户端代码
int main() {
std::cout << "=== Making Coffee ===\n";
std::unique_ptr<Beverage> coffee = std::make_unique<Coffee>();
coffee->prepareRecipe();
std::cout << "\n=== Making Tea ===\n";
std::unique_ptr<Beverage> tea = std::make_unique<Tea>();
tea->prepareRecipe();
return 0;
}
示例 2:数据处理管道
展示数据处理流程,不同的数据处理器有不同的解析和保存方式。
cpp
#include <iostream>
#include <string>
#include <vector>
// AbstractClass: 数据处理器
class DataProcessor {
public:
virtual ~DataProcessor() = default;
// 模板方法
void processData(const std::string& inputFile, const std::string& outputFile) final {
std::cout << "=== Starting data processing ===\n";
readData(inputFile);
validateData();
transformData();
if (shouldSave()) {
saveData(outputFile);
}
cleanup();
std::cout << "=== Data processing complete ===\n\n";
}
protected:
// 基本操作
virtual void readData(const std::string& filename) = 0;
virtual void transformData() = 0;
virtual void saveData(const std::string& filename) = 0;
// 具体操作
void validateData() {
std::cout << "Validating data integrity... OK\n";
}
void cleanup() {
std::cout << "Cleaning up temporary resources\n";
}
// 钩子方法
virtual bool shouldSave() const {
return true;
}
// 公共工具方法
void log(const std::string& message) {
std::cout << "[LOG] " << message << "\n";
}
};
// ConcreteClass: CSV处理器
class CsvProcessor : public DataProcessor {
private:
std::vector<std::string> data_;
protected:
void readData(const std::string& filename) override {
log("Reading CSV file: " + filename);
// 模拟读取CSV数据
data_ = {"row1,col1,col2", "row2,col1,col2", "row3,col1,col2"};
log("Read " + std::to_string(data_.size()) + " rows");
}
void transformData() override {
log("Transforming CSV data");
// 模拟数据处理
for (auto& row : data_) {
row += ",transformed";
}
}
void saveData(const std::string& filename) override {
log("Saving transformed data to: " + filename);
// 模拟保存操作
log("Saved " + std::to_string(data_.size()) + " rows");
}
};
// ConcreteClass: JSON处理器
class JsonProcessor : public DataProcessor {
private:
std::string data_;
protected:
void readData(const std::string& filename) override {
log("Reading JSON file: " + filename);
// 模拟读取JSON数据
data_ = R"({"key": "value", "array": [1, 2, 3]})";
}
void transformData() override {
log("Transforming JSON data");
// 模拟JSON转换
data_ = R"({"transformed": true, "data": )" + data_ + "}";
}
void saveData(const std::string& filename) override {
log("Saving JSON data to: " + filename);
// 模拟保存操作
log("JSON data saved successfully");
}
// 重写钩子方法
bool shouldSave() const override {
std::cout << "Save transformed JSON? (y/n): ";
char response;
std::cin >> response;
return response == 'y' || response == 'Y';
}
};
// 客户端代码
int main() {
CsvProcessor csvProcessor;
csvProcessor.processData("input.csv", "output.csv");
JsonProcessor jsonProcessor;
jsonProcessor.processData("data.json", "transformed.json");
return 0;
}
示例 3:游戏AI框架
展示游戏中的AI行为框架,不同的敌人有不同的具体行为。
cpp
#include <iostream>
#include <memory>
// AbstractClass: 游戏AI
class GameAI {
public:
virtual ~GameAI() = default;
// 模板方法
void performTurn() final {
std::cout << "--- " << getName() << "'s turn ---\n";
collectResources();
buildStructures();
buildUnits();
attack();
std::cout << "--- Turn complete ---\n\n";
}
protected:
virtual std::string getName() const = 0;
// 基本操作
virtual void collectResources() = 0;
virtual void buildStructures() = 0;
virtual void buildUnits() = 0;
// 具体操作
void attack() {
std::cout << "Default attack behavior\n";
}
// 钩子方法
virtual bool shouldBuildDefenses() const {
return false;
}
};
// ConcreteClass: 兽人AI
class OrcAI : public GameAI {
protected:
std::string getName() const override { return "Orc Warlord"; }
void collectResources() override {
std::cout << "Orcs are pillaging nearby villages for resources\n";
}
void buildStructures() override {
std::cout << "Building Orcish barracks and watchtowers\n";
}
void buildUnits() override {
std::cout << "Training grunts, trolls, and catapults\n";
}
// 重写攻击行为
void attack() {
std::cout << "Orcs launch a massive frontal assault!\n";
}
};
// ConcreteClass: 人类AI
class HumanAI : public GameAI {
protected:
std::string getName() const override { return "Human Commander"; }
void collectResources() override {
std::cout << "Humans are gathering gold and lumber from mines and forests\n";
}
void buildStructures() override {
std::cout << "Building farms, barracks, and blacksmiths\n";
if (shouldBuildDefenses()) {
std::cout << "Also building defensive walls and guard towers\n";
}
}
void buildUnits() override {
std::cout << "Training footmen, archers, and knights\n";
}
// 重写钩子方法
bool shouldBuildDefenses() const override {
return true; // 人类总是建造防御
}
};
// ConcreteClass: 亡灵AI
class UndeadAI : public GameAI {
protected:
std::string getName() const override { return "Necromancer"; }
void collectResources() override {
std::cout << "Raising corpses and harvesting blight crystals\n";
}
void buildStructures() override {
std::cout << "Constructing crypts, graveyards, and ziggurats\n";
}
void buildUnits() override {
std::cout << "Raising skeletons, ghouls, and abominations\n";
}
// 重写攻击行为
void attack() {
std::cout << "Undead swarm the enemy with endless waves of minions!\n";
}
};
// 客户端代码
int main() {
std::vector<std::unique_ptr<GameAI>> aiPlayers;
aiPlayers.push_back(std::make_unique<OrcAI>());
aiPlayers.push_back(std::make_unique<HumanAI>());
aiPlayers.push_back(std::make_unique<UndeadAI>());
// 模拟游戏回合
for (int turn = 1; turn <= 2; ++turn) {
std::cout << "=== TURN " << turn << " ===\n";
for (auto& ai : aiPlayers) {
ai->performTurn();
}
}
return 0;
}
- 模板方法模式的优缺点
优点:
代码复用:将公共行为移到父类,避免代码重复。
控制反转:父类控制整体流程,子类只关注具体实现。
扩展性好:容易添加新的具体类,符合开闭原则。
封装不变部分:将不变的行为封装在父类中。
提高可维护性:算法结构清晰,易于理解和维护。
缺点:
限制灵活性:模板方法定义了固定流程,可能限制子类的灵活性。
可能导致过多的子类:每个不同的实现都需要一个子类。
父类与子类耦合:子类需要了解父类的实现细节。
违反里氏替换原则:如果子类重写太多方法,可能违反该原则。
- 模板方法模式 vs 策略模式
模板方法模式:使用继承,在编译时确定算法结构,通过子类化来改变部分行为。
策略模式:使用组合,在运行时动态改变整个算法,更灵活但结构更复杂。
模板方法模式非常适合框架设计,它定义了操作的骨架,而将具体步骤的实现延迟到子类中。这种模式在需要定义算法框架但又希望保持一定灵活性的场景中非常有用。