1. 装饰器模式基础回顾
在开始讨论C++中的装饰器模式变体之前,我们需要先理解装饰器模式的基本概念。装饰器模式是一种结构型设计模式,它允许向现有对象动态添加新功能,同时不改变其结构。这种模式创建了一个装饰器类,用来包装原始类,并在保持类方法签名完整性的前提下提供额外的功能。
装饰器模式的核心思想是通过组合而非继承来扩展功能。在C++中,这通常意味着:
- 定义一个抽象基类(Component)声明核心接口
- 创建具体组件(ConcreteComponent)实现基础功能
- 定义装饰器基类(Decorator)继承自Component并持有Component指针
- 实现具体装饰器(ConcreteDecorator)添加特定功能
这种模式特别适合以下场景:
- 当需要在不影响其他对象的情况下,动态、透明地给单个对象添加职责
- 当不能采用继承扩展功能时(比如final类或需要避免类爆炸)
- 当功能扩展可能需要随意组合时
提示:装饰器模式与继承的关键区别在于,装饰器是在运行时动态添加功能,而继承是在编译时静态确定功能。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. C++中装饰器模式的经典实现
让我们先看一个C++中装饰器模式的经典实现示例,这是理解各种变体的基础。假设我们有一个图形绘制的场景:
cpp复制// 抽象组件
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};
// 具体组件
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a Circle" << std::endl;
}
};
// 装饰器基类
class ShapeDecorator : public Shape {
protected:
Shape* shape;
public:
ShapeDecorator(Shape* s) : shape(s) {}
void draw() override {
if (shape) shape->draw();
}
~ShapeDecorator() {
delete shape;
}
};
// 具体装饰器:红色边框
class RedBorderDecorator : public ShapeDecorator {
public:
RedBorderDecorator(Shape* s) : ShapeDecorator(s) {}
void draw() override {
ShapeDecorator::draw();
addRedBorder();
}
private:
void addRedBorder() {
std::cout << "Adding red border" << std::endl;
}
};
// 具体装饰器:阴影效果
class ShadowDecorator : public ShapeDecorator {
public:
ShadowDecorator(Shape* s) : ShapeDecorator(s) {}
void draw() override {
ShapeDecorator::draw();
addShadow();
}
private:
void addShadow() {
std::cout << "Adding shadow effect" << std::endl;
}
};
使用示例:
cpp复制Shape* circle = new Circle();
Shape* redCircle = new RedBorderDecorator(new Circle());
Shape* shadowRedCircle = new ShadowDecorator(new RedBorderDecorator(new Circle()));
circle->draw(); // 输出: Drawing a Circle
redCircle->draw(); // 输出: Drawing a Circle \n Adding red border
shadowRedCircle->draw();// 输出: Drawing a Circle \n Adding red border \n Adding shadow effect
delete circle;
delete redCircle;
delete shadowRedCircle;
这种经典实现有几个关键特点:
- 装饰器类继承自抽象组件类
- 装饰器类包含一个指向组件类的指针
- 具体装饰器在调用原始操作前后添加新行为
- 可以嵌套多个装饰器形成装饰链
3. C++装饰器模式的常见变体
在实际C++开发中,装饰器模式有多种变体形式,每种都有其适用场景和优缺点。让我们探讨几种最常见的变体。
3.1 模板装饰器
C++的模板特性允许我们创建更灵活、类型安全的装饰器。模板装饰器不需要继承自抽象组件类,而是通过模板参数接受任何符合特定接口的类型。
cpp复制template <typename T>
class LoggingDecorator {
T decorated;
public:
LoggingDecorator(T&& t) : decorated(std::forward<T>(t)) {}
void operation() {
std::cout << "Operation started at " << std::time(nullptr) << std::endl;
decorated.operation();
std::cout << "Operation completed at " << std::time(nullptr) << std::endl;
}
};
// 使用示例
class BasicService {
public:
void operation() {
std::cout << "Performing basic operation" << std::endl;
}
};
BasicService service;
LoggingDecorator<BasicService> loggedService(std::move(service));
loggedService.operation();
模板装饰器的优点:
- 不需要继承层次结构
- 编译时类型检查
- 可以装饰任何具有匹配接口的类型
- 避免了虚函数调用的开销
缺点:
- 无法在运行时动态改变装饰行为
- 类型信息必须在编译时已知
3.2 策略装饰器
这种变体将装饰行为抽象为策略对象,可以在运行时动态改变装饰逻辑。
cpp复制class DrawingStrategy {
public:
virtual void drawExtra() = 0;
virtual ~DrawingStrategy() = default;
};
class RedBorderStrategy : public DrawingStrategy {
public:
void drawExtra() override {
std::cout << "Adding red border" << std::endl;
}
};
class ShadowStrategy : public DrawingStrategy {
public:
void drawExtra() override {
std::cout << "Adding shadow" << std::endl;
}
};
class StrategyDecorator : public Shape {
Shape* shape;
std::unique_ptr<DrawingStrategy> strategy;
public:
StrategyDecorator(Shape* s, DrawingStrategy* ds)
: shape(s), strategy(ds) {}
void draw() override {
shape->draw();
if (strategy) strategy->drawExtra();
}
void setStrategy(DrawingStrategy* ds) {
strategy.reset(ds);
}
~StrategyDecorator() {
delete shape;
}
};
使用示例:
cpp复制Shape* circle = new Circle();
StrategyDecorator decoratedCircle(circle, new RedBorderStrategy());
decoratedCircle.draw(); // 使用红色边框策略
decoratedCircle.setStrategy(new ShadowStrategy());
decoratedCircle.draw(); // 现在使用阴影策略
策略装饰器的优势:
- 可以在运行时动态改变装饰行为
- 符合开闭原则(对扩展开放,对修改关闭)
- 装饰逻辑可以独立变化和复用
3.3 函数式装饰器
C++11引入的lambda和std::function使得函数式风格的装饰器成为可能。
cpp复制#include <functional>
#include <iostream>
using Operation = std::function<void()>;
Operation decorate(Operation op, std::function<void()> before,
std::function<void()> after) {
return [=] {
if (before) before();
if (op) op();
if (after) after();
};
}
// 使用示例
void basicOperation() {
std::cout << "Basic operation" << std::endl;
}
auto decoratedOp = decorate(
basicOperation,
[] { std::cout << "Before operation" << std::endl; },
[] { std::cout << "After operation" << std::endl; }
);
decoratedOp(); // 输出三行信息
函数式装饰器的特点:
- 轻量级,不需要定义类层次
- 非常适合装饰函数调用
- 可以方便地组合多个装饰器
- 语法简洁,适合一次性使用场景
3.4 CRTP装饰器
奇异递归模板模式(CRTP)可以用来实现静态多态的装饰器,兼具继承和模板的优点。
cpp复制template <typename Derived>
class DecoratorBase {
public:
void operation() {
static_cast<Derived*>(this)->beforeOperation();
std::cout << "Base operation" << std::endl;
static_cast<Derived*>(this)->afterOperation();
}
};
class ConcreteDecorator : public DecoratorBase<ConcreteDecorator> {
public:
void beforeOperation() {
std::cout << "Preparation work" << std::endl;
}
void afterOperation() {
std::cout << "Cleanup work" << std::endl;
}
};
// 使用示例
ConcreteDecorator decorator;
decorator.operation();
CRTP装饰器的优势:
- 静态多态,无虚函数开销
- 编译时检查装饰器是否实现了所需接口
- 可以构建复杂的编译时装饰器组合
4. 装饰器模式在C++标准库中的应用
C++标准库中有几个地方使用了装饰器模式的思想,虽然不是严格的GoF装饰器模式实现,但概念上是相似的。
4.1 IO流装饰器
C++的IO流库广泛使用了装饰器模式的思想。例如:
cpp复制#include <fstream>
#include <iomanip>
std::ofstream file("output.txt");
std::ostream& decoratedStream = std::setw(10) << std::hex << file;
decoratedStream << 255; // 将写入" ff"到文件
这里的std::setw和std::hex等IO操纵符实际上是在装饰基础流对象,为其添加额外的格式化功能。
4.2 STL容器适配器
STL中的stack、queue和priority_queue都是容器适配器,它们装饰了底层容器(如deque或vector),提供了不同的接口。
cpp复制#include <queue>
#include <vector>
// std::queue装饰了std::deque(默认)或其他序列容器
std::queue<int> q;
// 使用vector作为底层容器的队列
std::queue<int, std::vector<int>> vecQueue;
4.3 智能指针作为装饰器
C++的智能指针可以看作是对原始指针的装饰,添加了内存管理功能:
cpp复制#include <memory>
class Resource { /*...*/ };
void process() {
std::unique_ptr<Resource> decoratedPtr(new Resource());
// decoratedPtr 装饰了原始指针,添加了自动删除功能
}
5. 装饰器模式在现代C++中的最佳实践
现代C++(C++11/14/17/20)为装饰器模式带来了新的实现方式和优化可能。以下是几个最佳实践:
5.1 使用智能指针管理资源
传统的装饰器模式实现中,内存管理容易出错。现代C++应使用智能指针:
cpp复制class SafeShapeDecorator : public Shape {
std::unique_ptr<Shape> shape;
public:
SafeShapeDecorator(std::unique_ptr<Shape> s) : shape(std::move(s)) {}
void draw() override {
if (shape) shape->draw();
}
// 不再需要显式析构函数
};
5.2 移动语义优化
为装饰器实现移动语义可以提高性能:
cpp复制class MovableDecorator : public Shape {
std::unique_ptr<Shape> shape;
public:
MovableDecorator(std::unique_ptr<Shape> s) : shape(std::move(s)) {}
// 移动构造函数
MovableDecorator(MovableDecorator&& other) noexcept
: shape(std::move(other.shape)) {}
// 移动赋值运算符
MovableDecorator& operator=(MovableDecorator&& other) noexcept {
if (this != &other) {
shape = std::move(other.shape);
}
return *this;
}
void draw() override { /*...*/ }
};
5.3 可变参数模板装饰器
C++11的可变参数模板可以创建更灵活的装饰器工厂:
cpp复制template <typename T, typename... Decorators>
auto make_decorated(T&& base, Decorators&&... decorators) {
return std::forward<Decorators>(decorators)...(std::forward<T>(base));
}
// 使用示例
auto logger = [](auto f) {
return [f](auto&&... args) {
std::cout << "Logging call" << std::endl;
return f(std::forward<decltype(args)>(args)...);
};
};
auto add = [](int a, int b) { return a + b; };
auto loggedAdd = make_decorated(add, logger);
int result = loggedAdd(2, 3); // 输出日志并返回5
5.4 使用std::decay处理装饰器参数
当编写通用装饰器时,使用std::decay可以处理各种参数类型:
cpp复制template <typename F>
auto make_decorator(F&& f) {
return [f = std::forward<F>(f)](auto&&... args) {
std::cout << "Decorating..." << std::endl;
return f(std::forward<decltype(args)>(args)...);
};
}
6. 装饰器模式与其他设计模式的关系
装饰器模式常与其他设计模式结合使用或容易混淆,理解这些关系有助于正确应用。
6.1 装饰器 vs 适配器
关键区别:
- 装饰器不改变接口,只是扩展功能
- 适配器改变接口使其兼容
6.2 装饰器 vs 代理
相似点:
- 都包装另一个对象
- 都实现相同的接口
区别:
- 代理控制访问,通常不添加功能
- 装饰器总是添加功能
6.3 装饰器 vs 组合
关系:
- 装饰器可以看作是一种特殊形式的组合
- 装饰器通常只包装一个组件,而组合包含多个子组件
6.4 装饰器与策略模式结合
强大的组合:
- 使用策略模式决定装饰行为
- 装饰器包装策略对象
cpp复制class DrawingPolicy {
public:
virtual void apply() = 0;
virtual ~DrawingPolicy() = default;
};
class PolicyDecorator : public Shape {
std::unique_ptr<Shape> shape;
std::unique_ptr<DrawingPolicy> policy;
public:
PolicyDecorator(std::unique_ptr<Shape> s, std::unique_ptr<DrawingPolicy> p)
: shape(std::move(s)), policy(std::move(p)) {}
void draw() override {
shape->draw();
if (policy) policy->apply();
}
};
7. 装饰器模式的性能考量
在C++中使用装饰器模式时,性能是需要考虑的重要因素,特别是在高性能场景中。
7.1 虚函数开销
经典装饰器实现使用虚函数,这会带来:
- 每次调用额外的间接寻址
- 难以内联优化
- 虚表查找开销
解决方案:
- 对于性能关键路径,考虑模板装饰器
- 使用CRTP避免虚函数
- 将小函数标记为final帮助编译器优化
7.2 内存占用
装饰器模式会导致:
- 每个装饰层增加一个对象
- 动态分配的内存碎片
- 指针存储开销
优化方法:
- 使用内存池预分配装饰器对象
- 考虑就地构造装饰器
- 使用std::make_unique确保内存分配效率
7.3 缓存局部性
多层装饰可能导致:
- 数据分散在内存不同位置
- 缓存命中率降低
改进策略:
- 将频繁访问的数据放在一起
- 限制装饰层数
- 使用数组存储代替指针链
7.4 编译时装饰与运行时装饰
选择依据:
- 编译时装饰(模板、CRTP):高性能,无运行时开销
- 运行时装饰:灵活,可动态配置
实际项目中常常混合使用,关键路径用编译时装饰,可扩展部分用运行时装饰。
8. 实际项目中的装饰器模式应用案例
让我们看几个C++项目中装饰器模式的实际应用场景。
8.1 网络请求处理链
在网络库中,装饰器模式可用于构建灵活的处理管道:
cpp复制class RequestHandler {
public:
virtual void handle(Request& req) = 0;
virtual ~RequestHandler() = default;
};
class LoggingHandler : public RequestHandler {
std::unique_ptr<RequestHandler> next;
public:
LoggingHandler(std::unique_ptr<RequestHandler> h) : next(std::move(h)) {}
void handle(Request& req) override {
log(req);
if (next) next->handle(req);
}
private:
void log(const Request& req) {
// 记录请求日志
}
};
class CompressionHandler : public RequestHandler {
// 类似实现
};
// 构建处理链
auto handler = std::make_unique<LoggingHandler>(
std::make_unique<CompressionHandler>(
std::make_unique<CoreHandler>()
)
);
8.2 游戏中的Buff系统
游戏开发中,角色状态可以用装饰器模式实现:
cpp复制class Character {
public:
virtual int getAttack() const = 0;
virtual ~Character() = default;
};
class BasicCharacter : public Character {
public:
int getAttack() const override { return 10; }
};
class BuffDecorator : public Character {
std::unique_ptr<Character> character;
public:
BuffDecorator(std::unique_ptr<Character> c) : character(std::move(c)) {}
int getAttack() const override { return character->getAttack(); }
};
class StrengthBuff : public BuffDecorator {
public:
using BuffDecorator::BuffDecorator;
int getAttack() const override {
return BuffDecorator::getAttack() + 5;
}
};
class RageBuff : public BuffDecorator {
public:
using BuffDecorator::BuffDecorator;
int getAttack() const override {
return BuffDecorator::getAttack() * 2;
}
};
// 使用示例
auto hero = std::make_unique<RageBuff>(
std::make_unique<StrengthBuff>(
std::make_unique<BasicCharacter>()
)
);
int attack = hero->getAttack(); // (10 + 5) * 2 = 30
8.3 数据库访问层
在数据库访问层,装饰器可以添加缓存、日志、权限检查等功能:
cpp复制class Database {
public:
virtual Record query(const std::string& key) = 0;
virtual ~Database() = default;
};
class CacheDecorator : public Database {
std::unique_ptr<Database> db;
std::unordered_map<std::string, Record> cache;
public:
CacheDecorator(std::unique_ptr<Database> d) : db(std::move(d)) {}
Record query(const std::string& key) override {
if (cache.count(key)) return cache[key];
auto result = db->query(key);
cache[key] = result;
return result;
}
};
9. 测试装饰器模式的技巧
测试装饰器代码需要特别考虑装饰器的组合性和嵌套性。
9.1 单元测试策略
- 单独测试每个具体组件
- 单独测试每个装饰器
- 测试装饰器与组件的各种组合
cpp复制TEST(DecoratorTest, BasicComponent) {
Circle circle;
testing::internal::CaptureStdout();
circle.draw();
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(output, "Drawing a Circle\n");
}
TEST(DecoratorTest, SingleDecorator) {
Shape* redCircle = new RedBorderDecorator(new Circle());
testing::internal::CaptureStdout();
redCircle->draw();
std::string output = testing::internal::GetCapturedStdout();
EXPECT_TRUE(output.find("Drawing a Circle") != std::string::npos);
EXPECT_TRUE(output.find("Adding red border") != std::string::npos);
delete redCircle;
}
9.2 模拟对象测试
使用模拟对象测试装饰器:
cpp复制class MockShape : public Shape {
public:
MOCK_METHOD(void, draw, (), (override));
};
TEST(DecoratorTest, DecoratorCallsWrappedObject) {
auto mock = std::make_unique<MockShape>();
EXPECT_CALL(*mock, draw()).Times(1);
RedBorderDecorator decorator(mock.release());
decorator.draw();
}
9.3 性能测试
特别关注多层装饰时的性能:
cpp复制BENCHMARK(TemplateDecorator) {
TemplateDecoratedService service;
for (auto _ : state) {
service.operation();
}
}
BENCHMARK(ClassicDecorator) {
ClassicDecoratedService service;
for (auto _ : state) {
service.operation();
}
}
9.4 组合测试
测试装饰器的各种组合是否正确:
cpp复制TEST(DecoratorTest, MultipleDecoratorsOrder) {
Shape* decorated = new ShadowDecorator(new RedBorderDecorator(new Circle()));
testing::internal::CaptureStdout();
decorated->draw();
std::string output = testing::internal::GetCapturedStdout();
// 检查输出顺序是否正确
size_t circlePos = output.find("Circle");
size_t redPos = output.find("red border");
size_t shadowPos = output.find("shadow");
EXPECT_LT(circlePos, redPos);
EXPECT_LT(redPos, shadowPos);
delete decorated;
}
10. 装饰器模式的局限性与替代方案
虽然装饰器模式功能强大,但在某些情况下可能有更好的替代方案。
10.1 装饰器模式的局限性
- 过度使用会导致大量小类,增加系统复杂度
- 装饰器与组件接口必须一致,限制了灵活性
- 难以装饰需要访问私有成员的对象
- 多层装饰可能影响性能
10.2 替代方案:策略模式
当行为变化比添加功能更合适时:
cpp复制class StyledShape {
Shape* shape;
std::unique_ptr<StyleStrategy> style;
public:
void draw() {
shape->draw();
style->applyStyle();
}
// 可以动态改变style
};
10.3 替代方案:组合模式
当需要表示部分-整体层次结构时:
cpp复制class CompositeShape : public Shape {
std::vector<Shape*> children;
public:
void add(Shape* s) { children.push_back(s); }
void draw() override {
for (auto child : children) child->draw();
}
};
10.4 替代方案:访问者模式
当需要添加的操作变化频繁时:
cpp复制class ShapeVisitor {
public:
virtual void visit(Circle&) = 0;
virtual void visit(Square&) = 0;
};
class Shape {
public:
virtual void accept(ShapeVisitor&) = 0;
};
class DrawVisitor : public ShapeVisitor {
// 实现各种visit方法
};
10.5 替代方案:C++20概念
C++20概念可以约束模板装饰器,提供更好的接口保证:
cpp复制template <typename T>
concept Drawable = requires(T t) {
{ t.draw() } -> std::same_as<void>;
};
template <Drawable T>
class ConceptDecorator {
T decorated;
public:
void draw() {
// 装饰逻辑
decorated.draw();
}
};
11. C++23中装饰器模式的未来展望
随着C++标准的发展,装饰器模式可能会有新的实现方式和优化空间。
11.1 使用Deducing this简化CRTP
C++23的"deducing this"特性可以简化CRTP装饰器:
cpp复制template <typename Self>
class DecoratorBase {
public:
void operation(this Self&& self) {
self.beforeOperation();
std::cout << "Base operation" << std::endl;
self.afterOperation();
}
};
class ConcreteDecorator : public DecoratorBase<ConcreteDecorator> {
public:
void beforeOperation() { /*...*/ }
void afterOperation() { /*...*/ }
};
11.2 编译期反射与装饰器
未来的编译期反射可能实现更强大的装饰器:
cpp复制// 伪代码,假设的C++未来特性
template <typename T>
auto decorate(T obj) {
return metaclass {
// 自动生成装饰器代码
};
}
11.3 模式匹配与装饰器
模式匹配可以简化装饰器的处理逻辑:
cpp复制// 伪代码,假设的C++未来特性
void process(Shape& s) {
inspect (s) {
is RedBorderDecorator<Circle> => // 特殊处理
is ShadowDecorator<Shape> => // 其他处理
}
}
11.4 协程与异步装饰器
协程可以用于实现异步操作的装饰:
cpp复制template <typename Awaitable>
auto log_async(Awaitable a) -> decltype(a) {
std::cout << "Async operation started" << std::endl;
co_await a;
std::cout << "Async operation completed" << std::endl;
}
12. 从设计角度评估装饰器模式
让我们从软件设计原则的角度分析装饰器模式的优缺点。
12.1 符合的设计原则
- 单一职责原则:每个装饰器只关注一个特定功能
- 开闭原则:可以扩展功能而不修改现有代码
- 组合优于继承:通过组合动态添加功能
12.2 违反的设计原则
- 接口隔离原则:装饰器必须实现完整组件接口,即使只增强部分方法
- 迪米特法则:装饰器需要了解组件的内部细节
12.3 设计权衡
- 灵活性 vs 复杂性
- 运行时动态 vs 编译时安全
- 代码可维护性 vs 性能开销
12.4 何时选择装饰器模式
适合场景:
- 需要动态、透明地添加职责
- 职责可以相互独立地组合
- 不能或不方便使用继承
不适合场景:
- 需要改变对象接口
- 装饰逻辑需要访问私有成员
- 性能极其敏感的场合
13. 装饰器模式与其他语言的对比
了解其他语言中装饰器模式的实现有助于更好地在C++中应用。
13.1 Python的装饰器语法
Python有专门的装饰器语法:
python复制@log_call
@validate_args
def process_data(data):
# 实现
等效于:
python复制process_data = log_call(validate_args(process_data))
13.2 Java注解 vs C++装饰器
Java使用注解实现类似功能:
java复制@LogEntryExit
@ValidateParameters
public void process(Data data) {
// 实现
}
与C++的区别:
- Java注解通常是编译时处理
- C++装饰器是运行时对象组合
13.3 JavaScript高阶函数装饰器
JavaScript常用高阶函数实现装饰:
javascript复制function logDecorator(f) {
return function(...args) {
console.log(`Calling ${f.name}`);
return f.apply(this, args);
};
}
const decorated = logDecorator(originalFunction);
13.4 Go的装饰器模式
Go使用函数组合实现类似效果:
go复制func logDecorator(f func(int) int) func(int) int {
return func(x int) int {
fmt.Println("Before call")
result := f(x)
fmt.Println("After call")
return result
}
}
14. 装饰器模式的反模式与误用
了解装饰器模式的常见误用有助于避免设计陷阱。
14.1 装饰器链过长
问题:
- 超过3-4层的装饰器链难以理解和维护
- 性能下降明显
解决方案:
- 考虑重构为策略模式
- 合并相关装饰器
14.2 装饰器依赖特定顺序
问题:
- 装饰器应用顺序影响结果
- 难以测试和维护
解决方案:
- 使装饰器行为独立于顺序
- 或显式管理顺序(如使用建造者模式构建装饰链)
14.3 装饰器访问组件内部状态
问题:
- 破坏封装性
- 增加耦合度
解决方案:
- 通过组件接口公开必要状态
- 考虑访问者模式替代
14.4 装饰器修改组件行为而非扩展
问题:
- 装饰器应该添加新行为,而不是修改现有行为
- 可能导致不可预期的副作用
正确做法:
- 保持组件原始行为不变
- 只添加新功能
15. 装饰器模式在C++框架中的应用实例
许多流行的C++框架和库内部使用了装饰器模式的思想。
15.1 Boost.Asio中的装饰器
Boost.Asio使用装饰器模式处理IO操作:
cpp复制auto socket = std::make_shared<tcp::socket>(io_context);
async_write(*socket, buffer(data),
bind_executor(strand,
[socket](error_code ec, size_t length) {
// 处理完成
}
)
);
这里的bind_executor实际上装饰了完成处理程序。
15.2 Google Test中的测试装饰
Google Test支持测试装饰器:
cpp复制class Fixture : public ::testing::Test {
protected:
void SetUp() override { /*...*/ }
};
TEST_F(Fixture, Test1) { /*...*/ }
// 装饰所有测试
class LoggingListener : public ::testing::EmptyTestEventListener {
void OnTestStart(const ::testing::TestInfo&) override {
// 记录测试开始
}
};
// 注册装饰器
::testing::TestEventListeners& listeners =
::testing::UnitTest::GetInstance()->listeners();
listeners.Append(new LoggingListener);
15.3 Qt中的事件过滤器
Qt的事件过滤器是装饰器模式的变体:
cpp复制class EventFilter : public QObject {
Q_OBJECT
public:
bool eventFilter(QObject* watched, QEvent* event) override {
// 处理或修改事件
return QObject::eventFilter(watched, event);
}
};
QApplication app(argc, argv);
QLineEdit lineEdit;
EventFilter filter;
lineEdit.installEventFilter(&filter);
15.4 LLVM中的Pass管理器
LLVM编译器使用Pass装饰IR处理:
cpp复制// 创建Pass管理器
legacy::PassManager pm;
// 添加装饰Pass
pm.add(createVerifierPass());
pm.add(createCFGSimplificationPass());
pm.add(createInstructionCombiningPass());
// 运行Pass
pm.run(*module);
16. 元编程与装饰器模式的结合
C++强大的元编程能力可以与装饰器模式结合,创造更灵活的设计。
16.1 使用SFINAE约束装饰器
cpp复制template <typename T, typename = void>
struct is_drawable : std::false_type {};
template <typename T>
struct is_drawable<T, std::void_t<decltype(std::declval<T>().draw())>>
: std::true_type {};
template <typename T, typename = std::enable_if_t<is_drawable<T>::value>>
class SafeDecorator {
T decorated;
public:
void draw() {
// 安全装饰逻辑
decorated.draw();
}
};
16.2 编译时装饰器组合
使用模板元编程实现编译时装饰器组合:
cpp复制template <typename... Decorators>
struct Decorate;
template <typename T>
struct Decorate<T> : T {};
template <typename Head, typename... Tail>
struct Decorate<Head, Tail...> : Head, Decorate<Tail...> {};
using MyService = Decorate<Logging, Validation, Caching>;
16.3 使用constexpr计算装饰逻辑
C++11/14/17的constexpr可以在编译时计算装饰行为:
cpp复制template <int Level>
class LogLevelDecorator {
public:
constexpr void log(const char* msg) const {
if (Level >= currentLogLevel) {
std::cout << msg << std::endl;
}
}
};
16.4 使用变量模板简化装饰器创建
C++14变量模板可以简化装饰器实例化:
cpp复制template <typename T>
constexpr auto logging_decorator = LoggingDecorator<T>{};
auto service = logging_decorator<BasicService>;
17. 装饰器模式与多线程编程
在多线程环境中使用装饰器模式需要特别考虑线程安全问题。
17.1 线程安全的装饰器实现
确保装饰器操作是原子的:
cpp复制class ThreadSafeDecorator : public Shape {
std::unique_ptr<Shape> shape;
mutable std::mutex mtx;
public:
void draw() override {
std::lock_guard<std::mutex> lock(mtx);
if (shape) shape->draw();
}
};
17.2 装饰器与线程局部存储
使用thread_local实现线程特定的装饰:
cpp复制class ThreadLocalDecorator : public Shape {
static thread_local std::unique_ptr<Shape> tls_shape;
public:
void draw() override {
if (!tls_shape) tls_shape = createThreadLocalShape();
tls_shape->draw();
}
};
17.3 异步装饰器模式
使用future/promise实现异步装饰:
cpp复制template <typename F>
auto async_decorator(F f) {
return [f](auto&&... args) {
return std::async(std::launch::async, f,
std::forward<decltype(args)>(args)...);
};
}
17.4 装饰器与协程
C++20协程可以用于异步装饰器:
cpp复制template <typename Awaitable>
auto log_async(Awaitable a) -> Awaitable {
std::cout << "Start\n";
co_await a;
std::cout << "End\n";
}
18. 装饰器模式在嵌入式系统中的特殊考虑
在资源受限的嵌入式系统中使用装饰器模式需要特别设计。
18.1 静态内存装饰器
避免动态内存分配:
cpp复制template <typename Component>
class StaticDecorator {
Component component;
public:
template <typename... Args>
StaticDecorator(Args&&... args)
: component(std::forward<Args>(args)...) {}
void operation() {
// 装饰逻辑
component.operation();
}
};
18.2 无虚函数装饰器
使用CRTP避免虚函数开销:
cpp复制template <typename Derived>
class EmbeddedDecorator {
public:
void operation() {
static_cast<Derived*>(this)->before();
// 核心操作
static_cast<Derived*>(this)->after();
}
};
18.3 内存池装饰器
使用预分配内存池:
cpp复制class DecoratorPool {
static constexpr size_t POOL_SIZE = 10;
std::array<std::byte, sizeof(Decorator) * POOL_SIZE> pool;
// 管理逻辑...
};
auto decorator = DecoratorPool::allocate();
18.4 最小化装饰器大小
优化装饰器内存占用:
cpp复制class TinyDecorator : public Shape {
Shape* shape;
uint8_t flags; // 使用位域最小化存储
public:
// 实现...
};
19. 装饰器模式与C++惯用法的融合
将装饰器模式与C++特有惯用法结合可以产生更地道的实现。
19.1 RAII装饰器
利用RAII管理装饰资源:
cpp复制class RaiiDecorator {
std::unique_ptr<Resource> resource;
public:
RaiiDecorator() : resource(acquire_resource()) {}
~RaiiDecorator() { release_resource(resource.get()); }
void operation() {
// 使用resource
}
};
19.2 基于范围的装饰器
C++11范围for支持装饰迭代器:
cpp复制template <typename Container>
class RangeDecorator {
Container& c;
public:
auto begin() { return decorate_iterator(c.begin()); }
auto end() { return decorate_iterator(c.end()); }
};
19.3 类型擦除装饰器
使用std::function或any实现类型擦除:
cpp复制class AnyDecorator {
std::function<void()>
