目录
装饰模式(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++ 实现注意事项
接口一致性:
- 装饰器必须实现与被装饰对象相同的接口(继承同一抽象类)。
- 确保装饰器不改变接口签名,只增强功能。
智能指针管理:
// 使用智能指针避免内存泄漏 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++ 中实现对象功能动态扩展的重要工具,通过合理使用装饰器,可以构建出灵活、可维护的软件系统,同时避免继承带来的局限性。