1. 模式定义
装饰模式(Decorator Pattern)是一种结构型设计模式,允许动态地向一个现有对象添加新的功能,同时不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供额外的功能。
2. 核心特点
- 替代继承:提供比继承更灵活的扩展方式
- 透明性:装饰类与被装饰类具有相同的接口
- 动态性:运行时动态添加/撤销功能
- 开闭原则:对扩展开放,对修改关闭
3. 模式结构
4. 应用场景
典型场景:
- IO流处理(Java/C++流库)
- GUI组件(添加滚动条、边框等)
- 咖啡/披萨订单系统(添加配料)
- 权限系统(动态添加角色权限)
- 日志系统(不同格式/输出方式)
- 数据压缩/加密(透明附加功能)
实战案例:
- 游戏装备系统(武器附魔)
- 文本编辑器(字体、颜色、下划线等格式)
- 图片处理(滤镜、水印、边框)
5. C++代码示例
基础示例:咖啡订单系统高级示例:数据流处理6. 装饰模式 vs 继承
7. 优点与缺点
优点 ✅
- 灵活性:运行时动态添加/删除功能
- 避免类爆炸:无需为每个组合创建子类
- 单一职责:每个装饰类只负责一个功能
- 组合优于继承:更符合设计原则
缺点 ❌
- 增加复杂度:产生大量小类
- 调试困难:层层包装,追踪较难
- 类型识别问题:装饰后的对象类型发生变化
8. 最佳实践
- 保持接口一致:装饰类必须实现被装饰类的接口
- 避免过度装饰:装饰链不要超过3-4层
- 考虑使用工厂模式:创建复杂的装饰组合
- 注意性能影响:每层装饰都有额外开销
9.装饰模式与工厂模式的结合
装饰模式和工厂模式的结合是一种常见的设计模式组合,可以有效解决装饰对象的创建和管理问题。
9.1 为什么要结合?
9.2 结合方式
方式一:简单工厂 + 装饰模式- #include <iostream>
- #include <memory>
- #include <string>
- #include <map>
- // 饮料基类
- 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 "浓缩咖啡"; }
- double cost() const override { return 25.0; }
- };
- class Latte : public Beverage {
- public:
- std::string getDescription() const override { return "拿铁咖啡"; }
- double cost() const override { return 30.0; }
- };
- // 装饰器基类
- class CondimentDecorator : public Beverage {
- protected:
- std::unique_ptr<Beverage> beverage;
- public:
- CondimentDecorator(std::unique_ptr<Beverage> bev)
- : beverage(std::move(bev)) {}
- };
- // 具体装饰器
- class Milk : public CondimentDecorator {
- public:
- Milk(std::unique_ptr<Beverage> bev) : CondimentDecorator(std::move(bev)) {}
- std::string getDescription() const override {
- return beverage->getDescription() + " + 牛奶";
- }
- double cost() const override { return beverage->cost() + 5.0; }
- };
- class Mocha : public CondimentDecorator {
- public:
- Mocha(std::unique_ptr<Beverage> bev) : CondimentDecorator(std::move(bev)) {}
- std::string getDescription() const override {
- return beverage->getDescription() + " + 摩卡";
- }
- double cost() const override { return beverage->cost() + 8.0; }
- };
- // ========== 简单工厂 ==========
- class CoffeeFactory {
- public:
- enum CoffeeType { ESPRESSO, LATTE };
-
- static std::unique_ptr<Beverage> createCoffee(CoffeeType type) {
- switch (type) {
- case ESPRESSO: return std::make_unique<Espresso>();
- case LATTE: return std::make_unique<Latte>();
- default: return nullptr;
- }
- }
-
- // 预定义的组合
- static std::unique_ptr<Beverage> createSignatureLatte() {
- auto coffee = std::make_unique<Latte>();
- coffee = std::make_unique<Milk>(std::move(coffee));
- coffee = std::make_unique<Mocha>(std::move(coffee));
- return coffee;
- }
- };
- // 使用示例
- int main() {
- // 使用工厂创建基础咖啡
- auto coffee1 = CoffeeFactory::createCoffee(CoffeeFactory::ESPRESSO);
- coffee1 = std::make_unique<Milk>(std::move(coffee1));
- std::cout << coffee1->getDescription() << ": ¥" << coffee1->cost() << std::endl;
-
- // 使用工厂创建复杂组合
- auto signature = CoffeeFactory::createSignatureLatte();
- std::cout << signature->getDescription() << ": ¥" << signature->cost() << std::endl;
-
- return 0;
- }
复制代码方式二:工厂方法 + 装饰模式- #include <iostream>
- #include <memory>
- #include <vector>
- // 装饰器类型枚举
- enum class Topping { MILK, MOCHA, WHIP, SOY };
- // 抽象工厂
- class BeverageFactory {
- public:
- virtual ~BeverageFactory() = default;
- virtual std::unique_ptr<Beverage> createBeverage() = 0;
- virtual std::unique_ptr<Beverage> createWithToppings(
- const std::vector<Topping>& toppings) = 0;
- };
- // 具体工厂:浓缩咖啡工厂
- class EspressoFactory : public BeverageFactory {
- private:
- std::unique_ptr<Beverage> applyToppings(
- std::unique_ptr<Beverage> beverage,
- const std::vector<Topping>& toppings) {
-
- for (auto topping : toppings) {
- switch (topping) {
- case Topping::MILK:
- beverage = std::make_unique<Milk>(std::move(beverage));
- break;
- case Topping::MOCHA:
- beverage = std::make_unique<Mocha>(std::move(beverage));
- break;
- default:
- break;
- }
- }
- return beverage;
- }
-
- public:
- std::unique_ptr<Beverage> createBeverage() override {
- return std::make_unique<Espresso>();
- }
-
- std::unique_ptr<Beverage> createWithToppings(
- const std::vector<Topping>& toppings) override {
- auto beverage = createBeverage();
- return applyToppings(std::move(beverage), toppings);
- }
- };
- // 使用示例
- int main() {
- EspressoFactory factory;
-
- // 创建纯浓缩咖啡
- auto espresso = factory.createBeverage();
-
- // 创建加牛奶和摩卡的浓缩咖啡
- auto deluxe = factory.createWithToppings({Topping::MILK, Topping::MOCHA});
-
- std::cout << deluxe->getDescription() << ": ¥" << deluxe->cost() << std::endl;
-
- return 0;
- }
复制代码方式三:抽象工厂 + 装饰模式- #include <iostream>
- #include <memory>
- #include <map>
- #include <functional>
- // 装饰器创建函数类型
- using DecoratorCreator = std::function<std::unique_ptr<Beverage>(std::unique_ptr<Beverage>)>;
- // 装饰器注册表
- class DecoratorRegistry {
- private:
- std::map<std::string, DecoratorCreator> creators;
-
- public:
- void registerDecorator(const std::string& name, DecoratorCreator creator) {
- creators[name] = creator;
- }
-
- DecoratorCreator getCreator(const std::string& name) {
- auto it = creators.find(name);
- if (it != creators.end()) {
- return it->second;
- }
- return nullptr;
- }
- };
- // 全局注册表
- DecoratorRegistry& getRegistry() {
- static DecoratorRegistry registry;
- return registry;
- }
- // 自动注册类
- class AutoRegister {
- public:
- AutoRegister(const std::string& name, DecoratorCreator creator) {
- getRegistry().registerDecorator(name, creator);
- }
- };
- // 注册装饰器(使用静态初始化)
- static AutoRegister registerMilk("milk", [](std::unique_ptr<Beverage> b) {
- return std::make_unique<Milk>(std::move(b));
- });
- static AutoRegister registerMocha("mocha", [](std::unique_ptr<Beverage> b) {
- return std::make_unique<Mocha>(std::move(b));
- });
- // 抽象工厂:支持配置驱动
- class ConfigurableCoffeeFactory {
- private:
- std::string baseCoffee;
- std::vector<std::string> toppings;
-
- public:
- ConfigurableCoffeeFactory(const std::string& coffee,
- const std::vector<std::string>& toppings)
- : baseCoffee(coffee), toppings(toppings) {}
-
- std::unique_ptr<Beverage> create() {
- std::unique_ptr<Beverage> beverage;
-
- // 创建基础咖啡
- if (baseCoffee == "espresso") {
- beverage = std::make_unique<Espresso>();
- } else if (baseCoffee == "latte") {
- beverage = std::make_unique<Latte>();
- } else {
- return nullptr;
- }
-
- // 动态应用装饰器
- for (const auto& topping : toppings) {
- auto creator = getRegistry().getCreator(topping);
- if (creator) {
- beverage = creator(std::move(beverage));
- }
- }
-
- return beverage;
- }
- };
- // 使用示例:类似配置文件解析
- int main() {
- // 模拟从配置文件读取
- std::map<std::string, std::vector<std::string>> orders = {
- {"order1", {"espresso", "milk", "mocha"}},
- {"order2", {"latte", "milk"}},
- {"order3", {"espresso", "milk", "milk", "mocha"}} // 双份牛奶
- };
-
- for (const auto& [orderId, config] : orders) {
- if (config.size() < 1) continue;
-
- std::string coffeeType = config[0];
- std::vector<std::string> toppings(config.begin() + 1, config.end());
-
- ConfigurableCoffeeFactory factory(coffeeType, toppings);
- auto beverage = factory.create();
-
- if (beverage) {
- std::cout << orderId << ": " << beverage->getDescription()
- << " | ¥" << beverage->cost() << std::endl;
- }
- }
-
- return 0;
- }
复制代码方式四:建造者模式结合- #include <iostream>
- #include <memory>
- #include <vector>
- // 装饰器构建器
- class CoffeeBuilder {
- private:
- std::unique_ptr<Beverage> beverage;
-
- public:
- CoffeeBuilder& setBase(std::unique_ptr<Beverage> base) {
- beverage = std::move(base);
- return *this;
- }
-
- CoffeeBuilder& addMilk() {
- if (beverage) {
- beverage = std::make_unique<Milk>(std::move(beverage));
- }
- return *this;
- }
-
- CoffeeBuilder& addMocha() {
- if (beverage) {
- beverage = std::make_unique<Mocha>(std::move(beverage));
- }
- return *this;
- }
-
- CoffeeBuilder& addWhip() {
- if (beverage) {
- beverage = std::make_unique<Whip>(std::move(beverage));
- }
- return *this;
- }
-
- std::unique_ptr<Beverage> build() {
- return std::move(beverage);
- }
- };
- // 工厂类使用建造者
- class CoffeeShop {
- public:
- static std::unique_ptr<Beverage> createStandardLatte() {
- return CoffeeBuilder()
- .setBase(std::make_unique<Latte>())
- .addMilk()
- .build();
- }
-
- static std::unique_ptr<Beverage> createDeluxeMocha() {
- return CoffeeBuilder()
- .setBase(std::make_unique<Espresso>())
- .addMilk()
- .addMocha()
- .addWhip()
- .build();
- }
-
- // 支持自定义
- static std::unique_ptr<Beverage> customOrder(
- std::unique_ptr<Beverage> base,
- bool hasMilk, bool hasMocha, bool hasWhip) {
-
- CoffeeBuilder builder;
- builder.setBase(std::move(base));
-
- if (hasMilk) builder.addMilk();
- if (hasMocha) builder.addMocha();
- if (hasWhip) builder.addWhip();
-
- return builder.build();
- }
- };
- int main() {
- auto latte = CoffeeShop::createStandardLatte();
- std::cout << latte->getDescription() << ": ¥" << latte->cost() << std::endl;
-
- auto mocha = CoffeeShop::createDeluxeMocha();
- std::cout << mocha->getDescription() << ": ¥" << mocha->cost() << std::endl;
-
- auto custom = CoffeeShop::customOrder(
- std::make_unique<Latte>(),
- true, true, false // 加牛奶和摩卡,不加奶泡
- );
- std::cout << custom->getDescription() << ": ¥" << custom->cost() << std::endl;
-
- return 0;
- }
复制代码3. 结合的优势对比
4. 最佳实践建议
| 场景 | 推荐方式 | 原因 |
|---|
| 装饰器种类少(<5种) | 简单工厂 | 实现简单,够用 | | 装饰器有层次关系 | 工厂方法 | 支持继承扩展 | | 需要灵活配置 | 抽象工厂+注册表 | 配置驱动,热插拔 | | 复杂构建过程 | 建造者模式 | 链式调用,可读性强 |
5. 实际应用场景- // 实战:UI组件装饰工厂
- class UIComponentFactory {
- public:
- static std::unique_ptr<Component> createStyledButton(
- const std::string& text,
- const std::vector<std::string>& styles) {
-
- auto button = std::make_unique<Button>(text);
-
- for (const auto& style : styles) {
- if (style == "border") {
- button = std::make_unique<BorderDecorator>(std::move(button));
- } else if (style == "shadow") {
- button = std::make_unique<ShadowDecorator>(std::move(button));
- } else if (style == "rounded") {
- button = std::make_unique<RoundedDecorator>(std::move(button));
- }
- }
-
- return button;
- }
- };
- // 使用
- auto btn = UIComponentFactory::createStyledButton("Click Me",
- {"border", "shadow", "rounded"});
复制代码这种结合方式既保持了装饰模式的灵活性,又通过工厂模式解决了对象创建的复杂度问题,是实际项目中的常用组合。 |