【C++设计模式】第五篇:装饰器模式
C++设计模式系列文章目录
【C++设计模式】第一篇 C++单例模式–懒汉与饿汉以及线程安全
【C++设计模式】第二篇:策略模式(Strategy)–从基本介绍,内部原理、应用场景、使用方法,常见问题和解决方案进行深度解析
【C++设计模式】第三篇:观察者模式(别名:发布-订阅模式、模型-视图模式、源-监听器模式)
【C++设计模式】第五篇:装饰器模式
【C++设计模式】第五篇:装饰器模式
- C++设计模式系列文章目录
- 一、模式核心概念与结构
- 二、C++ 实现示例:咖啡与配料的装饰
- 三、装饰模式与继承的对比
- 四、应用场景
- 五、C++ 实现注意事项
- 六、装饰模式与其他设计模式的关系
- 七、实战案例:网络请求处理链
- 八、优缺点分析
- 九、C++ 标准库中的装饰模式应用
- C++设计模式之装饰器模式(decorator)(结构型)
- 一、模式动机
原文链接:https://blog.csdn.net/AAADiao/article/details/148846165
装饰模式(Decorator Pattern)是一种【结构型】设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式通过创建一个装饰器类,来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。装饰模式在不使用继承的情况下,实现了对象功能的动态扩展。
一、模式核心概念与结构
装饰模式包含四个核心角色:
抽象组件(Component):定义对象的接口,可以给这些对象动态地添加职责。
具体组件(Concrete Component):实现抽象组件接口,定义具体的对象,装饰器可以给它增加额外的职责。
抽象装饰器(Decorator):继承自抽象组件,并持有一个抽象组件的引用,用于装饰具体组件。
具体装饰器(Concrete Decorator):实现抽象装饰器的方法,在调用具体组件方法的前后,添加额外的功能。
二、C++ 实现示例:咖啡与配料的装饰
以下是一个经典的装饰模式示例,演示如何动态添加咖啡的配料:
#include <iostream>
#include <string>
#include <memory>// 抽象组件:饮料
class Beverage {
public:virtual ~Beverage() {}virtual std::string getDescription() const = 0;virtual double cost() const = 0;
};// 具体组件:浓缩咖啡
class Espresso : public Beverage {
public:std::string getDescription() const override {return "Espresso";}double cost() const override {return 1.99;}
};// 具体组件:黑咖啡
class DarkRoast : public Beverage {
public:std::string getDescription() const override {return "Dark Roast Coffee";}double cost() const override {return 0.99;}
};// 抽象装饰器:配料
class CondimentDecorator : public Beverage {
protected:std::shared_ptr<Beverage> beverage; // 持有被装饰对象的引用public:CondimentDecorator(std::shared_ptr<Beverage> b) : beverage(b) {}
};// 具体装饰器:牛奶
class Milk : public CondimentDecorator {
public:Milk(std::shared_ptr<Beverage> b) : CondimentDecorator(b) {}std::string getDescription() const override {return beverage->getDescription() + ", Milk";}double cost() const override {return beverage->cost() + 0.30;}
};// 具体装饰器:摩卡
class Mocha : public CondimentDecorator {
public:Mocha(std::shared_ptr<Beverage> b) : CondimentDecorator(b) {}std::string getDescription() const override {return beverage->getDescription() + ", Mocha";}double cost() const override {return beverage->cost() + 0.45;}
};// 客户端代码
int main() {// 纯浓缩咖啡std::shared_ptr<Beverage> beverage = std::make_shared<Espresso>();std::cout << beverage->getDescription() << " $" << beverage->cost() << std::endl;// 加牛奶的黑咖啡std::shared_ptr<Beverage> beverage2 = std::make_shared<DarkRoast>();beverage2 = std::make_shared<Milk>(beverage2);std::cout << beverage2->getDescription() << " $" << beverage2->cost() << std::endl;// 加双份摩卡的浓缩咖啡std::shared_ptr<Beverage> beverage3 = std::make_shared<Espresso>();beverage3 = std::make_shared<Mocha>(beverage3);beverage3 = std::make_shared<Mocha>(beverage3);std::cout << beverage3->getDescription() << " $" << beverage3->cost() << std::endl;return 0;
}
三、装饰模式与继承的对比
传统继承方式的局限性:
- 功能扩展通过创建子类实现,导致类数量爆炸。
- 功能扩展是静态的,编译时确定,无法在运行时动态调整。
装饰模式的优势:
- 动态组合对象功能,运行时灵活扩展。
- 避免继承导致的类层次过深问题。
- 符合开闭原则:无需修改原有代码,即可添加新的装饰器。
四、应用场景
- 动态添加功能:当需要给对象动态添加功能,且不影响其他对象时,例如:
- 图形界面组件的边框、滚动条、阴影效果。
- 网络请求的加密、压缩、缓存功能。
- 替代多重继承:当使用继承会导致类爆炸时,例如:
- 文件流的缓冲、加密、压缩处理。
- 游戏角色的装备、技能组合。
- 功能增强链:当需要按顺序执行多个功能增强时,例如:
- 日志处理(过滤、格式化、存储)。
- HTTP 请求处理(身份验证、参数解析、权限检查)。
五、C++ 实现注意事项
- 接口一致性:
装饰器必须实现与被装饰对象相同的接口(继承同一抽象类)。
确保装饰器不改变接口签名,只增强功能。
2. 智能指针管理:
// 使用智能指针避免内存泄漏
std::shared_ptr<Beverage> beverage = std::make_shared<Espresso>();
beverage = std::make_shared<Mocha>(beverage);
- 初始化顺序:
- 装饰器的初始化顺序可能影响最终结果,需谨慎设计。
- 避免重复装饰:
- 某些场景需防止对同一对象重复应用相同装饰器。
六、装饰模式与其他设计模式的关系
- 适配器模式:适配器模式详解
- 适配器模式改变对象接口,装饰模式增强对象功能。
- 适配器模式是 “适配”,装饰模式是 “增强”。
- 代理模式:
- 代理模式控制对象访问,装饰模式增加对象功能。
- 代理模式的重点是访问控制,装饰模式的重点是功能扩展。
- 建造者模式:建造者模式详解
- 建造者模式分步构建复杂对象,装饰模式动态增强对象。
- 建造者模式关注对象构建过程,装饰模式关注对象运行时功能。
七、实战案例:网络请求处理链
以下是一个网络请求处理链的装饰模式实现:
#include <iostream>
#include <string>
#include <memory>// 抽象组件:请求处理器
class RequestHandler {
public:virtual ~RequestHandler() {}virtual void handleRequest(const std::string& request) const = 0;
};// 具体组件:基础请求处理器
class BaseRequestHandler : public RequestHandler {
public:void handleRequest(const std::string& request) const override {std::cout << "Base handler processing request: " << request << std::endl;}
};// 抽象装饰器:请求处理器装饰器
class RequestHandlerDecorator : public RequestHandler {
protected:std::shared_ptr<RequestHandler> handler;public:RequestHandlerDecorator(std::shared_ptr<RequestHandler> h) : handler(h) {}
};// 具体装饰器:日志记录
class LoggingDecorator : public RequestHandlerDecorator {
public:LoggingDecorator(std::shared_ptr<RequestHandler> h) : RequestHandlerDecorator(h) {}void handleRequest(const std::string& request) const override {std::cout << "Logging: Request received - " << request << std::endl;handler->handleRequest(request);std::cout << "Logging: Request processed" << std::endl;}
};// 具体装饰器:权限检查
class AuthDecorator : public RequestHandlerDecorator {
public:AuthDecorator(std::shared_ptr<RequestHandler> h) : RequestHandlerDecorator(h) {}void handleRequest(const std::string& request) const override {std::cout << "Auth: Checking permissions..." << std::endl;handler->handleRequest(request);std::cout << "Auth: Permissions checked" << std::endl;}
};// 客户端代码
int main() {// 创建基础处理器std::shared_ptr<RequestHandler> baseHandler = std::make_shared<BaseRequestHandler>();// 添加日志和权限装饰std::shared_ptr<RequestHandler> authHandler = std::make_shared<AuthDecorator>(baseHandler);std::shared_ptr<RequestHandler> loggingAuthHandler = std::make_shared<LoggingDecorator>(authHandler);// 处理请求loggingAuthHandler->handleRequest("GET /api/data");return 0;
}
八、优缺点分析
优点:
- 灵活扩展:可以在运行时动态添加或删除功能。
- 单一职责:每个装饰器专注于一个特定功能,符合单一职责原则。
- 开闭原则:无需修改现有代码即可添加新装饰器。
缺点:
- 复杂性增加:多层装饰会导致系统复杂,调试困难。
- 对象嵌套:过多装饰器可能导致对象嵌套过深,影响性能。
- 接口一致性:装饰器必须严格遵循组件接口,否则可能破坏系统。
九、C++ 标准库中的装饰模式应用
- 输入 / 输出流(iostream):
- std::basic_ios是抽象组件,std::ifstream、std::ofstream是具体组件。
- std::ios_base::iword和std::ios_base::pword可用于动态添加流的属性。
- 智能指针:
- std::shared_ptr和std::unique_ptr可视为对原始指针的装饰器,添加了内存管理功能。
- STL 迭代器适配器:
- std::reverse_iterator、std::insert_iterator等是对基础迭代器的装饰。
装饰模式是 C++ 中实现对象功能动态扩展的重要工具,通过合理使用装饰器,可以构建出灵活、可维护的软件系统,同时避免继承带来的局限性。
C++设计模式之装饰器模式(decorator)(结构型)
一、模式动机
比如,给自家宠物小狗的画框需要不同的颜色等。