设计模式:装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许动态地向对象添加新的行为,而无需修改其原始类代码。
模式定义
装饰器模式通过将对象放入包含行为的特殊封装对象中来为原对象动态添加新的行为。
模式结构
装饰器模式包含以下主要角色:
-
Component(抽象组件):定义对象的接口,可以动态地添加职责
-
ConcreteComponent(具体组件):定义具体的对象,可以为其添加职责
-
Decorator(抽象装饰类):继承或实现Component,并包含一个Component的引用
-
ConcreteDecorator(具体装饰类):向组件添加具体职责
C++实现示例
#include <iostream>
#include <string>
// 抽象组件
class Beverage {
public:
virtual ~Beverage() = default;
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 CondimentDecorator : public Beverage {
protected:
Beverage* beverage;
public:
CondimentDecorator(Beverage* beverage) : beverage(beverage) {}
std::string getDescription() const override {
return beverage->getDescription();
}
double cost() const override {
return beverage->cost();
}
};
// 具体装饰类 - 摩卡
class Mocha : public CondimentDecorator {
public:
Mocha(Beverage* beverage) : CondimentDecorator(beverage) {}
std::string getDescription() const override {
return beverage->getDescription() + ", Mocha";
}
double cost() const override {
return beverage->cost() + 0.20;
}
};
// 具体装饰类 - 奶泡
class Whip : public CondimentDecorator {
public:
Whip(Beverage* beverage) : CondimentDecorator(beverage) {}
std::string getDescription() const override {
return beverage->getDescription() + ", Whip";
}
double cost() const override {
return beverage->cost() + 0.10;
}
};
int main() {
// 创建基础饮料
Beverage* beverage = new Espresso();
std::cout << beverage->getDescription() << " $" << beverage->cost() << std::endl;
// 添加装饰
Beverage* beverage2 = new Espresso();
beverage2 = new Mocha(beverage2); // 添加摩卡
beverage2 = new Whip(beverage2); // 添加奶泡
std::cout << beverage2->getDescription() << " $" << beverage2->cost() << std::endl;
delete beverage;
delete beverage2;
return 0;
}
输出结果
Espresso $1.99 Espresso, Mocha, Whip $2.29
模式优点
-
灵活性:比继承更灵活,可以在运行时动态添加或删除功能
-
避免子类膨胀:不需要通过创建大量子类来扩展功能
-
符合开闭原则:无需修改原有代码即可扩展功能
-
可以多层装饰:可以多次装饰一个对象,添加多个功能
模式缺点
-
复杂性增加:会增加许多小类,可能使程序结构复杂
-
调试困难:多层装饰可能使调试变得困难
-
过度使用:可能导致设计过度复杂,应适度使用
装饰器模式实现日志系统(C++)
下面将展示如何使用装饰器模式构建一个灵活的日志系统,可以动态添加不同的日志功能(如时间戳、严重级别、日志来源等)。
设计思路
-
基础日志组件:提供最基本的日志功能
-
装饰器类:可以添加额外功能(时间戳、级别等)
-
组合使用:可以任意组合装饰器来定制日志输出
完整实现代码
#include <iostream>
#include <string>
#include <memory>
#include <ctime>
// 抽象组件:日志接口
class Logger {
public:
virtual ~Logger() = default;
virtual void log(const std::string& message) = 0;
};
// 具体组件:基础日志
class BasicLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << message << std::endl;
}
};
// 抽象装饰器
class LoggerDecorator : public Logger {
protected:
std::unique_ptr<Logger> logger;
public:
LoggerDecorator(std::unique_ptr<Logger> logger)
: logger(std::move(logger)) {}
void log(const std::string& message) override {
logger->log(message);
}
};
// 具体装饰器:添加时间戳
class TimestampLogger : public LoggerDecorator {
public:
TimestampLogger(std::unique_ptr<Logger> logger)
: LoggerDecorator(std::move(logger)) {}
void log(const std::string& message) override {
time_t now = time(nullptr);
std::string timeStr = ctime(&now);
timeStr.pop_back(); // 移除换行符
logger->log("[" + timeStr + "] " + message);
}
};
// 具体装饰器:添加日志级别
class LevelLogger : public LoggerDecorator {
std::string level;
public:
LevelLogger(std::unique_ptr<Logger> logger, const std::string& level)
: LoggerDecorator(std::move(logger)), level(level) {}
void log(const std::string& message) override {
logger->log("[" + level + "] " + message);
}
};
// 具体装饰器:添加来源模块
class ModuleLogger : public LoggerDecorator {
std::string module;
public:
ModuleLogger(std::unique_ptr<Logger> logger, const std::string& module)
: LoggerDecorator(std::move(logger)), module(module) {}
void log(const std::string& message) override {
logger->log("[" + module + "] " + message);
}
};
// 使用示例
int main() {
// 1. 基础日志
auto basicLogger = std::make_unique<BasicLogger>();
basicLogger->log("这是一个基础日志消息");
// 2. 带时间戳的日志
auto timestampLogger = std::make_unique<TimestampLogger>(
std::make_unique<BasicLogger>()
);
timestampLogger->log("这是一个带时间戳的日志消息");
// 3. 带级别和时间戳的日志
auto levelTimestampLogger = std::make_unique<LevelLogger>(
std::make_unique<TimestampLogger>(
std::make_unique<BasicLogger>()
),
"INFO"
);
levelTimestampLogger->log("这是一个带级别和时间戳的日志消息");
// 4. 完整功能的日志(模块+级别+时间戳)
auto fullLogger = std::make_unique<ModuleLogger>(
std::make_unique<LevelLogger>(
std::make_unique<TimestampLogger>(
std::make_unique<BasicLogger>()
),
"WARNING"
),
"Network"
);
fullLogger->log("网络连接超时");
return 0;
}
输出示例
这是一个基础日志消息 [Thu Jun 15 14:30:45 2023] 这是一个带时间戳的日志消息 [INFO] [Thu Jun 15 14:30:45 2023] 这是一个带级别和时间戳的日志消息 [Network] [WARNING] [Thu Jun 15 14:30:45 2023] 网络连接超时
设计优势
-
灵活组合:可以任意组合装饰器,如只需要时间戳、或时间戳+级别等
-
易于扩展:添加新功能只需新增装饰器类,无需修改现有代码
-
单一职责:每个装饰器只关注一个功能的添加
-
运行时配置:可以在运行时决定使用哪些装饰器
进一步改进方向
-
添加文件日志装饰器:将日志输出到文件而非控制台
-
添加网络日志装饰器:将日志发送到远程服务器
-
添加格式化装饰器:支持JSON、XML等不同格式
-
添加过滤装饰器:根据级别过滤日志
关键点总结
-
基础组件(
BasicLogger)实现核心功能 -
装饰器继承自相同的接口(
Logger) -
每个装饰器在调用基础功能前后添加自己的行为
-
装饰器可以嵌套使用,形成处理链
这种设计使得日志系统非常灵活,可以根据不同需求动态组合各种日志功能。
装饰器模式通俗总结
装饰器模式就像给手机贴膜加壳——不改变手机本身,但能不断添加新功能。
核心思想:不修改原对象,通过"包装"方式动态添加功能
适用场景
-
需要在不影响其他对象的情况下,动态、透明地添加职责
-
需要撤销或动态添加职责
-
不能采用继承或继承不实际的情况下(如final类)
生活比喻:
-
好比穿衣服:
-
你本人是"基础组件"
-
每件衣服都是"装饰器"(T恤、外套、围巾)
-
可以自由搭配,天冷多穿几件,天热少穿几件
-
穿脱衣服不会改变你本人
-
-
就像汉堡包:
-
面包是基础
-
可以随意加生菜、芝士、肉饼(每层都是装饰器)
-
最终汉堡 = 基础面包 + 各种装饰配料
-
三大特点:
-
动态扩展:运行时才决定加什么功能(像游戏里随时换装备)
-
透明包装:装饰后用法和原对象完全一样(加了壳的手机还是用同样方式操作)
-
无限套娃:可以一层套一层(手机→保护壳→指环扣→贴纸)
开发中典型应用:
-
日志系统(基础日志+时间戳装饰+级别装饰)
-
IO流处理(基础流+缓冲装饰+加密装饰)
-
游戏角色装备(基础角色+武器装饰+防具装饰)
一句话记住:
"要扩展功能别改原类,套个装饰器最安全"
与其他模式的关系
-
适配器模式:装饰器改变对象的接口,适配器提供不同的接口
-
组合模式:装饰器可以被视为只有一个组件的组合
-
策略模式:装饰器改变对象的外表,策略模式改变对象的内核
装饰器模式提供了一种灵活的方式来扩展对象功能,是继承的一个有力替代方案。
更多推荐


所有评论(0)