模板方法模式 (Template Method)
模板方法模式 是一种行为型设计模式,它定义了一个操作中的算法骨架,将某些步骤的实现延迟到子类。通过模板方法,子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
意图
- 在一个方法中定义算法的骨架,而将某些具体步骤延迟到子类中实现。
- 让子类可以在不改变算法整体结构的情况下重新定义算法的某些步骤。
使用场景
- 多个类有相同的操作步骤,但具体实现不同:
- 当多个类的操作有一致的结构,但实现不同,可以使用模板方法模式。
- 希望控制算法的结构:
- 父类定义算法骨架,子类实现具体步骤。
- 避免代码重复:
- 将公共逻辑抽取到父类中,具体逻辑在子类中实现。
参与者角色
- 抽象类 (AbstractClass)
- 定义算法的骨架,并包含一个或多个抽象方法,子类需要实现这些方法。
- 提供一个模板方法,定义算法的整体结构。
- 具体类 (ConcreteClass)
- 实现抽象类中的抽象方法,提供算法的具体步骤。
示例代码
以下代码展示了模板方法模式的实现,用于模拟制作饮品的过程。不同的饮品(如茶和咖啡)有相同的制作步骤,但某些步骤的实现不同。
#include <iostream>
// 抽象类:饮品
class Beverage {
public:
virtual ~Beverage() = default;
// 模板方法:定义饮品制作的算法骨架
void prepareRecipe() {
boilWater(); // 1. 煮沸水
brew(); // 2. 冲泡
pourInCup(); // 3. 倒入杯中
if (customerWantsCondiments()) { // 4. 是否添加调料
addCondiments(); // 添加调料
}
}
protected:
// 基本操作
void boilWater() {
std::cout << "将水煮沸。
";
}
void pourInCup() {
std::cout << "将饮品倒入杯中。
";
}
// 抽象操作:由子类实现
virtual void brew() = 0;
virtual void addCondiments() = 0;
// Hook(钩子方法):子类可选择重写
virtual bool customerWantsCondiments() {
return true; // 默认需要添加调料
}
};
// 具体类:茶
class Tea : public Beverage {
protected:
void brew() override {
std::cout << "用热水浸泡茶叶。
";
}
void addCondiments() override {
std::cout << "添加柠檬。
";
}
};
// 具体类:咖啡
class Coffee : public Beverage {
protected:
void brew() override {
std::cout << "用热水冲泡咖啡。
";
}
void addCondiments() override {
std::cout << "添加牛奶和糖。
";
}
// 重写钩子方法
bool customerWantsCondiments() override {
char answer;
std::cout << "是否需要添加牛奶和糖?(y/n): ";
std::cin >> answer;
return (answer == 'y' || answer == 'Y');
}
};
// 客户端代码
int main() {
std::cout << "制作茶:
";
Tea tea;
tea.prepareRecipe();
std::cout << "
制作咖啡:
";
Coffee coffee;
coffee.prepareRecipe();
return 0;
}
代码解析
1. 抽象类 (Beverage)
- 定义了模板方法
prepareRecipe
,它包含制作饮品的算法骨架。 - 提供了基本操作(如
boilWater
和pourInCup
),以及抽象操作(如brew
和addCondiments
),具体实现由子类完成。 - 包含钩子方法
customerWantsCondiments
,允许子类自定义是否执行某些步骤。
class Beverage {
public:
virtual ~Beverage() = default;
void prepareRecipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
protected:
void boilWater() { std::cout << "将水煮沸。
"; }
void pourInCup() { std::cout << "将饮品倒入杯中。
"; }
virtual void brew() = 0;
virtual void addCondiments() = 0;
virtual bool customerWantsCondiments() { return true; }
};
2. 具体类 (Tea, Coffee)
Tea
:- 实现了
brew
和addCondiments
方法,定义了茶的制作方式。
- 实现了
Coffee
:- 实现了
brew
和addCondiments
方法,定义了咖啡的制作方式。 - 重写了
customerWantsCondiments
方法,允许用户选择是否添加调料。
- 实现了
class Tea : public Beverage {
protected:
void brew() override { std::cout << "用热水浸泡茶叶。
"; }
void addCondiments() override { std::cout << "添加柠檬。
"; }
};
3. 客户端代码
- 客户端通过调用模板方法
prepareRecipe
来制作饮品,具体实现由子类决定。
int main() {
Tea tea;
tea.prepareRecipe();
Coffee coffee;
coffee.prepareRecipe();
}
优缺点
优点
- 复用代码:
- 将公共逻辑抽取到模板方法中,减少代码重复。
- 灵活性:
- 子类可以通过实现抽象方法或重写钩子方法来自定义算法的某些步骤。
- 控制算法结构:
- 父类定义算法结构,保证子类不会破坏算法整体逻辑。
缺点
- 对子类的依赖:
- 子类需要实现抽象方法,可能导致类的数量增加。
- 模板方法难以扩展:
- 如果模板方法需要修改,可能会影响所有子类。
适用场景
- 多个子类有相同的算法结构,但实现不同:
- 如制作不同饮品的过程。
- 希望控制算法的执行流程:
- 父类定义算法骨架,子类实现具体步骤。
- 需要扩展算法的某些步骤:
- 通过子类实现具体步骤,保证算法结构不变。
总结
模板方法模式通过定义算法的骨架,将具体实现延迟到子类中,从而实现代码复用和灵活扩展。它特别适用于算法结构固定但某些步骤实现不同的场景。