文章目录
模板模式
什么是模板模式
模板模式(Template Method Pattern)是一种行为设计模式,它定义了一个操作中的算法骨架,将某些步骤的具体实现延迟到子类中。模板模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤,从而实现代码复用和扩展性。
使用场景包括:
存在一组相似的操作:当有一组操作拥有相同的算法结构,但是某些步骤的实现细节各不相同的时候,可以使用模板模式来定义这些操作的通用部分,并将变化的部分留给子类去实现。 算法结构固定,部分步骤可变:如果你希望在不改变算法整体结构的前提下,允许子类自由地扩展或修改某些步骤,模板模式是个很好的选择。这样可以保证算法的主体逻辑一致,同时提供必要的定制化能力。 封装算法细节:当需要将算法的实现细节封装起来,仅暴露一个高层接口给调用者使用时,模板模式可以帮助隐藏复杂的内部实现,提高代码的可读性和维护性。 代码复用和扩展:如果你发现有很多重复的代码,只是其中某些小部分逻辑不同,可以考虑使用模板模式提取公共部分到父类中,通过子类化来实现差异化的部分。
示例应用:
UI框架中的渲染流程,如所有页面都有加载、渲染内容、处理事件的基本流程,但具体的内容渲染逻辑由各个页面子类实现。 数据库访问框架中,可能有一个通用的数据库操作流程(连接数据库、发送查询、处理结果、关闭连接),而具体查询的SQL语句则由子类提供。 在框架或库的开发中,如Java并发包中的AbstractQueuedSynchronizer(AQS)使用模板方法模式定义了获取和释放锁的模板方法,具体实现留给子类(如ReentrantLock)。
为什么使用模板模式
在抽象类中统一操作步骤,并规定好接口,让子类实现接口。这样可以把各个具体的, 子类和操作步骤解耦合。你只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时,可使用模板方法模式。当多个类的算法除一些细微不同之外几乎完全一样时, 你可使用该模式。但其后果就是,只要算法发生变化, 你就可能需要修改所有的类。
模板模式实现步骤
1.提供一个抽象类:负责规定好接口,让子类实现接口,定义一个模板方法
2.提供具体实现子类:实现抽象类的抽象接口即可
示例
cpp
#include <iostream>
// 抽象基类,定义模板方法和基本操作
class CoffeeMaker {
public:
virtual ~CoffeeMaker() {}
// 模板方法:冲泡咖啡的步骤
void brewCoffee() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
protected:
// 基本操作,留给子类实现
virtual void brew() = 0;
virtual void addCondiments() = 0;
// 可能不需要子类重写的辅助方法
void boilWater() {
std::cout << "Boiling water" << std::endl;
}
void pourInCup() {
std::cout << "Pouring into cup" << std::endl;
}
// 可能需要子类决定的策略方法
virtual bool customerWantsCondiments() {
return true;
}
};
// 具体子类:黑咖啡
class BlackCoffee : public CoffeeMaker {
protected:
void brew() override {
std::cout << "Brewing black coffee" << std::endl;
}
void addCondiments() override {
// 黑咖啡不加调料
}
};
// 具体子类:加糖加奶咖啡
class SugarMilkCoffee : public CoffeeMaker {
protected:
void brew() override {
std::cout << "Brewing coffee with sugar and milk" << std::endl;
}
void addCondiments() override {
std::cout << "Adding sugar and milk" << std::endl;
}
bool customerWantsCondiments() override {
return true;
}
};
int main() {
CoffeeMaker* maker = new BlackCoffee();
maker->brewCoffee();
delete maker;
std::cout << "\n";
maker = new SugarMilkCoffee();
maker->brewCoffee();
delete maker;
return 0;
}
在这个例子中,CoffeeMaker类是抽象基类,它定义了冲泡咖啡的基本步骤作为模板方法(brewCoffee),并声明了两个纯虚函数brew和addCondiments作为基本操作,让子类去实现。BlackCoffee和SugarMilkCoffee是具体子类,它们实现了不同的冲泡方式和添加调料的逻辑。通过这种方式,我们可以复用通用的冲泡流程,同时允许子类灵活地改变某些步骤,符合模板方法模式的设计理念。
模板模式优缺点
优点
- 你可仅允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小
- 你可将重复代码提取到一个超类中
缺点
- 部分客户端可能会受到算法框架的限制
- 通过子类抑制默认步骤实现可能会导致违反里氏替换原则
- 模板方法中的步骤越多, 其维护工作就可能会越困难