1. 装饰器模式基础回顾
在C++中,装饰器模式是一种结构型设计模式,它允许我们动态地向对象添加新的行为,而无需修改其原始类。这种模式通过将对象包装在装饰器类的实例中来实现功能扩展,完美遵循了开闭原则(对扩展开放,对修改关闭)。
装饰器模式的核心在于:
- 所有装饰器和被装饰对象都继承自同一个抽象基类
- 装饰器持有被装饰对象的引用
- 装饰器可以在调用被装饰对象方法前后添加额外行为
cpp复制// 抽象组件接口
class Component {
public:
virtual ~Component() {}
virtual void operation() = 0;
};
// 具体组件实现
class ConcreteComponent : public Component {
public:
void operation() override {
std::cout << "基础操作" << std::endl;
}
};
// 抽象装饰器
class Decorator : public Component {
protected:
Component* component;
public:
Decorator(Component* c) : component(c) {}
void operation() override {
if(component) component->operation();
}
};
// 具体装饰器A
class ConcreteDecoratorA : public Decorator {
public:
ConcreteDecoratorA(Component* c) : Decorator(c) {}
void operation() override {
std::cout << "装饰器A前置操作" << std::endl;
Decorator::operation();
std::cout << "装饰器A后置操作" << std::endl;
}
};
2. 高级应用场景解析
2.1 动态属性扩展
在实际项目中,我们经常遇到需要为对象动态添加属性的需求。装饰器模式为此提供了优雅的解决方案。例如在游戏开发中,角色装备系统可以完美利用装饰器模式:
cpp复制// 角色装备系统示例
class Character {
public:
virtual int getAttack() const = 0;
virtual int getDefense() const = 0;
};
class Warrior : public Character {
public:
int getAttack() const override { return 10; }
int getDefense() const override { return 5; }
};
class Equipment : public Character {
protected:
Character* character;
public:
Equipment(Character* c) : character(c) {}
};
class Sword : public Equipment {
public:
Sword(Character* c) : Equipment(c) {}
int getAttack() const override {
return character->getAttack() + 15;
}
int getDefense() const override {
return character->getDefense();
}
};
class Shield : public Equipment {
public:
Shield(Character* c) : Equipment(c) {}
int getAttack() const override {
return character->getAttack();
}
int getDefense() const override {
return character->getDefense() + 10;
}
};
// 使用示例
Character* warrior = new Warrior();
warrior = new Sword(warrior); // 装备剑
warrior = new Shield(warrior); // 装备盾
2.2 流式接口装饰
装饰器模式特别适合实现流式接口,这在构建复杂配置对象时非常有用。例如构建HTTP请求:
cpp复制class HttpRequest {
public:
virtual std::string build() const = 0;
};
class BasicRequest : public HttpRequest {
std::string url;
public:
BasicRequest(const std::string& u) : url(u) {}
std::string build() const override {
return "GET " + url + " HTTP/1.1\r\n";
}
};
class RequestDecorator : public HttpRequest {
protected:
HttpRequest* request;
public:
RequestDecorator(HttpRequest* r) : request(r) {}
};
class WithHeader : public RequestDecorator {
std::string key, value;
public:
WithHeader(HttpRequest* r, const std::string& k, const std::string& v)
: RequestDecorator(r), key(k), value(v) {}
std::string build() const override {
return request->build() + key + ": " + value + "\r\n";
}
};
class WithBody : public RequestDecorator {
std::string body;
public:
WithBody(HttpRequest* r, const std::string& b)
: RequestDecorator(r), body(b) {}
std::string build() const override {
return request->build() + "\r\n" + body;
}
};
// 使用示例
HttpRequest* req = new BasicRequest("/api/data");
req = new WithHeader(req, "Content-Type", "application/json");
req = new WithHeader(req, "Authorization", "Bearer token");
req = new WithBody(req, "{\"key\":\"value\"}");
3. 性能优化技巧
3.1 内存管理策略
装饰器模式的一个常见问题是内存管理。由于装饰器链会创建多个对象,不当的内存管理可能导致内存泄漏。以下是几种优化策略:
- 使用智能指针管理生命周期:
cpp复制std::shared_ptr<Component> component = std::make_shared<ConcreteComponent>();
component = std::make_shared<ConcreteDecoratorA>(component);
- 实现移动语义减少拷贝:
cpp复制class Decorator {
std::unique_ptr<Component> component;
public:
Decorator(std::unique_ptr<Component>&& c)
: component(std::move(c)) {}
// ...
};
- 对象池模式复用装饰器:
cpp复制class DecoratorPool {
std::vector<std::unique_ptr<Decorator>> pool;
public:
Decorator* acquire(Component* c) {
if(pool.empty()) {
return new ConcreteDecoratorA(c);
}
auto d = std::move(pool.back());
pool.pop_back();
d->reset(c);
return d.release();
}
void release(Decorator* d) {
pool.emplace_back(d);
}
};
3.2 虚函数调用优化
装饰器模式会引入多层虚函数调用,可能影响性能。可以通过以下方式优化:
- 使用CRTP(Curiously Recurring Template Pattern)减少虚函数开销:
cpp复制template <typename T>
class Decorator : public Component {
Component* component;
public:
std::string operation() const override {
return static_cast<const T*>(this)->decorate(component->operation());
}
};
class ConcreteDecorator : public Decorator<ConcreteDecorator> {
public:
std::string decorate(const std::string& s) const {
return "Decorated: " + s;
}
};
- 内联小型装饰器:
cpp复制class InlineDecorator : public Decorator {
public:
InlineDecorator(Component* c) : Decorator(c) {}
std::string operation() const override {
// 简单操作直接内联实现
return "[" + Decorator::operation() + "]";
}
};
4. 复杂系统中的应用实践
4.1 日志系统装饰
在构建日志系统时,装饰器模式可以灵活地组合各种日志功能:
cpp复制class Logger {
public:
virtual ~Logger() = default;
virtual void log(const std::string& message) = 0;
};
class FileLogger : public Logger {
std::ofstream file;
public:
FileLogger(const std::string& filename) : file(filename) {}
void log(const std::string& message) override {
file << message << std::endl;
}
};
class LoggerDecorator : public Logger {
protected:
Logger* logger;
public:
LoggerDecorator(Logger* l) : logger(l) {}
};
class TimestampLogger : public LoggerDecorator {
public:
TimestampLogger(Logger* l) : LoggerDecorator(l) {}
void log(const std::string& message) override {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
logger->log(std::ctime(&time) + std::string(": ") + message);
}
};
class LevelLogger : public LoggerDecorator {
std::string level;
public:
LevelLogger(Logger* l, const std::string& lvl)
: LoggerDecorator(l), level(lvl) {}
void log(const std::string& message) override {
logger->log("[" + level + "] " + message);
}
};
// 使用示例
Logger* logger = new FileLogger("app.log");
logger = new TimestampLogger(logger);
logger = new LevelLogger(logger, "INFO");
logger->log("Application started");
4.2 权限控制系统
装饰器模式也非常适合实现细粒度的权限控制:
cpp复制class DataAccess {
public:
virtual std::string read() = 0;
virtual void write(const std::string& data) = 0;
};
class DatabaseAccess : public DataAccess {
public:
std::string read() override {
return "Sensitive data from database";
}
void write(const std::string& data) override {
std::cout << "Writing to database: " << data << std::endl;
}
};
class AccessControl : public DataAccess {
protected:
DataAccess* access;
std::string userRole;
public:
AccessControl(DataAccess* a, const std::string& role)
: access(a), userRole(role) {}
};
class ReadOnlyAccess : public AccessControl {
public:
ReadOnlyAccess(DataAccess* a, const std::string& role)
: AccessControl(a, role) {}
std::string read() override {
return access->read();
}
void write(const std::string&) override {
throw std::runtime_error("Write access denied for role: " + userRole);
}
};
class EncryptedAccess : public AccessControl {
public:
EncryptedAccess(DataAccess* a, const std::string& role)
: AccessControl(a, role) {}
std::string read() override {
std::string data = access->read();
return decrypt(data);
}
void write(const std::string& data) override {
access->write(encrypt(data));
}
private:
std::string encrypt(const std::string& data) {
// 简单加密示例
return "ENCRYPTED(" + data + ")";
}
std::string decrypt(const std::string& data) {
// 简单解密示例
if(data.find("ENCRYPTED(") == 0) {
return data.substr(10, data.size() - 11);
}
return data;
}
};
// 使用示例
DataAccess* db = new DatabaseAccess();
db = new ReadOnlyAccess(db, "guest");
db = new EncryptedAccess(db, "guest");
5. 设计考量与最佳实践
5.1 接口设计原则
在设计装饰器模式时,遵循以下接口设计原则至关重要:
-
保持装饰器接口精简:装饰器接口应该只包含真正需要装饰的方法,避免"接口污染"。
-
使用纯虚接口:基类应该是纯虚的,强制子类实现所有必要方法。
-
考虑接口隔离:如果装饰器只需要修改部分方法,可以考虑将大接口拆分为多个小接口。
cpp复制// 不好的设计:接口过于庞大
class Monster {
public:
virtual void attack() = 0;
virtual void defend() = 0;
virtual void move() = 0;
virtual void speak() = 0;
// ... 许多其他方法
};
// 更好的设计:接口分离
class Attacker {
public:
virtual void attack() = 0;
};
class Defender {
public:
virtual void defend() = 0;
};
// 装饰器只需要装饰关心的接口
class FireAttack : public Attacker {
Attacker* attacker;
public:
FireAttack(Attacker* a) : attacker(a) {}
void attack() override {
std::cout << "Fire effect!" << std::endl;
attacker->attack();
}
};
5.2 装饰器组合策略
在实际应用中,装饰器的组合顺序可能会影响最终行为。需要考虑以下策略:
-
无顺序依赖:装饰器可以以任意顺序组合,行为保持一致。
-
顺序敏感:某些装饰器需要在特定位置才能正常工作。
-
互斥装饰器:某些装饰器不能同时使用。
cpp复制// 顺序敏感示例
class LoggingDecorator : public Component {
public:
void operation() override {
std::cout << "Entering operation" << std::endl;
Component::operation();
std::cout << "Exiting operation" << std::endl;
}
};
class TimingDecorator : public Component {
public:
void operation() override {
auto start = std::chrono::high_resolution_clock::now();
Component::operation();
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Operation took "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< "ms" << std::endl;
}
};
// 正确的组合顺序:Timing应该包裹Logging
Component* c = new ConcreteComponent();
c = new LoggingDecorator(c);
c = new TimingDecorator(c); // 会测量包含日志的整个操作时间
5.3 测试策略
装饰器模式的测试需要考虑以下方面:
-
单元测试每个具体装饰器。
-
测试装饰器组合的正确性。
-
验证装饰器链的内存安全性。
cpp复制// 使用Google Test框架的示例
TEST(DecoratorPatternTest, SingleDecorator) {
Component* component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
testing::internal::CaptureStdout();
component->operation();
std::string output = testing::internal::GetCapturedStdout();
EXPECT_TRUE(output.find("ConcreteDecoratorA") != std::string::npos);
delete component;
}
TEST(DecoratorPatternTest, DecoratorChain) {
Component* component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
component = new ConcreteDecoratorB(component);
testing::internal::CaptureStdout();
component->operation();
std::string output = testing::internal::GetCapturedStdout();
EXPECT_TRUE(output.find("ConcreteDecoratorA") != std::string::npos);
EXPECT_TRUE(output.find("ConcreteDecoratorB") != std::string::npos);
delete component;
}
TEST(DecoratorPatternTest, MemorySafety) {
std::shared_ptr<Component> component = std::make_shared<ConcreteComponent>();
component = std::make_shared<ConcreteDecoratorA>(component);
component = std::make_shared<ConcreteDecoratorB>(component);
// 不需要手动delete,shared_ptr会自动管理内存
SUCCEED();
}
6. 与其他模式的比较与结合
6.1 装饰器 vs 继承
装饰器模式提供了比继承更灵活的功能扩展方式:
| 特性 | 继承 | 装饰器模式 |
|---|---|---|
| 扩展方式 | 编译时静态绑定 | 运行时动态组合 |
| 功能组合 | 通过多重继承实现,复杂 | 通过嵌套装饰器实现,简单 |
| 类数量 | 每个组合都需要新子类 | 少量装饰器类可组合出多种行为 |
| 修改已有代码 | 需要修改继承关系 | 无需修改已有代码 |
6.2 装饰器 vs 策略模式
装饰器和策略模式都涉及组合对象,但目的不同:
- 装饰器:增强现有行为,保持接口不变
- 策略:完全改变算法或策略,可能改变接口
cpp复制// 策略模式示例
class CompressionStrategy {
public:
virtual std::vector<uint8_t> compress(const std::vector<uint8_t>& data) = 0;
};
class ZipCompression : public CompressionStrategy {
public:
std::vector<uint8_t> compress(const std::vector<uint8_t>& data) override {
// 实现ZIP压缩
return data; // 简化示例
}
};
// 装饰器模式示例
class DataStream {
public:
virtual std::vector<uint8_t> read() = 0;
};
class CompressedStream : public DataStream {
DataStream* stream;
CompressionStrategy* compressor;
public:
CompressedStream(DataStream* s, CompressionStrategy* c)
: stream(s), compressor(c) {}
std::vector<uint8_t> read() override {
auto data = stream->read();
return compressor->compress(data);
}
};
// 两者可以结合使用
DataStream* stream = new FileStream("data.bin");
CompressionStrategy* strategy = new ZipCompression();
stream = new CompressedStream(stream, strategy);
6.3 装饰器与代理模式结合
装饰器模式常与代理模式结合使用,实现更复杂的功能:
cpp复制class Image {
public:
virtual void display() = 0;
};
class RealImage : public Image {
std::string filename;
public:
RealImage(const std::string& file) : filename(file) {
loadFromDisk();
}
void display() override {
std::cout << "Displaying " << filename << std::endl;
}
private:
void loadFromDisk() {
std::cout << "Loading " << filename << " from disk" << std::endl;
}
};
// 代理:控制访问
class ImageProxy : public Image {
RealImage* realImage;
std::string filename;
public:
ImageProxy(const std::string& file) : filename(file), realImage(nullptr) {}
void display() override {
if(!realImage) {
realImage = new RealImage(filename);
}
realImage->display();
}
};
// 装饰器:添加功能
class WatermarkDecorator : public Image {
Image* image;
std::string watermark;
public:
WatermarkDecorator(Image* img, const std::string& wm)
: image(img), watermark(wm) {}
void display() override {
image->display();
std::cout << "Adding watermark: " << watermark << std::endl;
}
};
// 结合使用
Image* img = new ImageProxy("photo.jpg");
img = new WatermarkDecorator(img, "Copyright 2023");
img->display(); // 延迟加载并添加水印
7. 现代C++中的改进实现
7.1 使用可变参数模板
现代C++的可变参数模板可以让装饰器实现更加灵活:
cpp复制template <typename... Decorators>
class DecoratorChain : public Component {
std::tuple<Decorators...> decorators;
public:
DecoratorChain(Decorators... decs) : decorators(decs...) {}
std::string operation() const override {
return applyDecorators(std::index_sequence_for<Decorators...>{});
}
private:
template <std::size_t... Is>
std::string applyDecorators(std::index_sequence<Is...>) const {
std::string result = "BaseComponent";
// 折叠表达式应用所有装饰器
((result = std::get<Is>(decorators).decorate(result)), ...);
return result;
}
};
// 具体装饰器实现
class BoldDecorator {
public:
std::string decorate(const std::string& s) const {
return "<b>" + s + "</b>";
}
};
class ColorDecorator {
std::string color;
public:
ColorDecorator(const std::string& c) : color(c) {}
std::string decorate(const std::string& s) const {
return "<span style='color:" + color + "'>" + s + "</span>";
}
};
// 使用示例
DecoratorChain<BoldDecorator, ColorDecorator> chain(
BoldDecorator{},
ColorDecorator{"red"}
);
std::cout << chain.operation(); // 输出: <span style='color:red'><b>BaseComponent</b></span>
7.2 使用Concept约束接口
C++20的Concept可以更好地约束装饰器接口:
cpp复制template <typename T>
concept ComponentConcept = requires(T t) {
{ t.operation() } -> std::convertible_to<std::string>;
};
template <ComponentConcept T>
class ModernDecorator {
T component;
public:
ModernDecorator(T&& comp) : component(std::forward<T>(comp)) {}
std::string operation() const {
return "Decorated(" + component.operation() + ")";
}
};
// 使用示例
class ModernComponent {
public:
std::string operation() const { return "Modern"; }
};
ModernDecorator<ModernComponent> decorator(ModernComponent{});
std::cout << decorator.operation(); // 输出: Decorated(Modern)
7.3 使用Lambda表达式
C++11引入的Lambda表达式可以实现轻量级装饰器:
cpp复制#include <functional>
#include <memory>
class Component {
public:
virtual ~Component() = default;
virtual void operation() = 0;
};
using DecoratorFunc = std::function<void(std::function<void()>)>;
class LambdaDecorator : public Component {
std::unique_ptr<Component> component;
DecoratorFunc decorator;
public:
LambdaDecorator(std::unique_ptr<Component>&& c, DecoratorFunc d)
: component(std::move(c)), decorator(d) {}
void operation() override {
decorator([this]() { component->operation(); });
}
};
// 使用示例
auto loggingDecorator = [](std::function<void()> f) {
std::cout << "Before operation" << std::endl;
f();
std::cout << "After operation" << std::endl;
};
std::unique_ptr<Component> component = std::make_unique<ConcreteComponent>();
component = std::make_unique<LambdaDecorator>(std::move(component), loggingDecorator);
component->operation();
8. 实际项目经验分享
8.1 避免过度使用装饰器
虽然装饰器模式很强大,但过度使用会导致一些问题:
-
调试困难:多层装饰器会使调用栈变得很深,增加调试难度。
-
性能开销:每层装饰器都会引入额外的函数调用开销。
-
代码可读性:嵌套的装饰器可能降低代码的可读性。
建议:
- 对于性能关键路径,考虑其他优化方式
- 限制装饰器嵌套深度(通常不超过3-4层)
- 为复杂装饰器链添加清晰的文档说明
8.2 装饰器工厂模式
在实际项目中,可以使用工厂模式来管理装饰器的创建:
cpp复制class DecoratorFactory {
public:
enum DecoratorType {
LOGGING,
TIMING,
CACHING
};
static std::unique_ptr<Component> createDecorator(
DecoratorType type,
std::unique_ptr<Component>&& component
) {
switch(type) {
case LOGGING:
return std::make_unique<LoggingDecorator>(std::move(component));
case TIMING:
return std::make_unique<TimingDecorator>(std::move(component));
case CACHING:
return std::make_unique<CachingDecorator>(std::move(component));
default:
throw std::invalid_argument("Unknown decorator type");
}
}
};
// 使用示例
std::unique_ptr<Component> component = std::make_unique<ConcreteComponent>();
component = DecoratorFactory::createDecorator(
DecoratorFactory::LOGGING,
std::move(component)
);
component = DecoratorFactory::createDecorator(
DecoratorFactory::TIMING,
std::move(component)
);
8.3 装饰器与依赖注入
在现代C++项目中,装饰器模式常与依赖注入框架结合使用:
cpp复制// 使用Boost.DI示例
#include <boost/di.hpp>
namespace di = boost::di;
class Service {
public:
virtual void execute() = 0;
};
class RealService : public Service {
public:
void execute() override {
std::cout << "RealService execution" << std::endl;
}
};
class LoggingDecorator : public Service {
std::shared_ptr<Service> service;
public:
LoggingDecorator(std::shared_ptr<Service> s) : service(s) {}
void execute() override {
std::cout << "Before execution" << std::endl;
service->execute();
std::cout << "After execution" << std::endl;
}
};
// 配置依赖注入容器
auto injector = di::make_injector(
di::bind<Service>().to<LoggingDecorator>(),
di::bind<Service>().to<RealService>().in(di::unique)
);
// 获取装饰后的服务
auto service = injector.create<std::shared_ptr<Service>>();
service->execute();
