装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰器类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
介绍
核心概念
- 组件接口(Component Interface):定义了原始对象和装饰器的共同接口,确保装饰器可以替代原始对象。
- 具体组件(Concrete Component):实现了组件接口的原始对象,提供基本功能。
- 抽象装饰器(Decorator):持有一个组件对象的引用,并实现了组件接口,以便可以装饰其他装饰器或具体组件。
- 具体装饰器(Concrete Decorator):扩展了抽象装饰器的功能,为组件添加额外职责。
- 装饰器嵌套机制 :通过beverage = std::make_shared<Whip>(beverage); / beverage = std::make_unique<Whip>(std::move(beverage))创建装饰器链,调用时会逐层委托到基础组件
关键特性
- 动态扩展:在运行时而非编译时添加功能。
- 递归组合:装饰器可以嵌套多层,形成一个责任链。
- 透明性:客户端无需关心对象是原始组件还是装饰后的组件。
- 遵循开闭原则:对扩展开放,对修改关闭。
主要优点
- 灵活性:比继承更灵活,可以在运行时动态组合功能。
- 单一职责:每个装饰器只关注一个特定功能,符合单一职责原则。
- 不修改原有代码:遵循开闭原则(对扩展开放,对修改关闭)。
装饰模式的使用时机
- 需要动态添加功能:当你需要在运行时为对象添加或删除功能,而不是在编译时通过继承实现。
- 避免子类爆炸:当使用继承会导致大量子类(如不同功能组合)时,装饰模式可以替代继承,减少类的数量。
- 功能可插拔:当你希望功能可以被独立启用或禁用时,装饰器可以自由组合。
实现
装饰器模式通过组合而非继承来扩展功能,为设计提供了更大的灵活性,是替代大量子类继承的有效解决方案。
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <string>
#include <memory>
// 抽象组件接口
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:
    std::unique_ptr<Beverage> beverage;
    
public:
    CondimentDecorator(std::unique_ptr<Beverage> b) : beverage(std::move(b)) {}
};
// 具体装饰器
class Mocha : public CondimentDecorator {
public:
    using CondimentDecorator::CondimentDecorator;
    
    std::string getDescription() const override {
        return beverage->getDescription() + ", Mocha";
    }
    
    double cost() const override {
        return beverage->cost() + 0.20;
    }
};
class Whip : public CondimentDecorator {
public:
    using CondimentDecorator::CondimentDecorator;
    
    std::string getDescription() const override {
        return beverage->getDescription() + ", Whip";
    }
    
    double cost() const override {
        return beverage->cost() + 0.10;
    }
};
int main() {
    // 创建基础饮料
    auto beverage = std::make_unique<Espresso>();
    std::cout << beverage->getDescription() 
              << " $" << beverage->cost() << std::endl;
    
    // 添加装饰
    beverage = std::make_unique<Mocha>(std::move(beverage));
    beverage = std::make_unique<Whip>(std::move(beverage));
    
    std::cout << beverage->getDescription() 
              << " $" << beverage->cost() << std::endl;
    
    return 0;
}典型应用场景
- IO流处理 :Java的InputStream/OutputStream、Python的file对象包装。
- GUI组件:为窗口、按钮等添加滚动条、边框等功能。
- 缓存策略:在数据访问层添加缓存装饰器。
- 日志与事务:在业务逻辑外围添加日志记录或事务管理。
- 游戏角色装备系统:动态为角色添加武器、防具等属性。
优化
- 隐藏具体装饰器实现。
- 简化客户端代码。
- 支持更灵活的装饰器组合。
- 增强代码可维护性。
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <functional>
// 抽象组件接口
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 HouseBlend : public Beverage {
public:
    std::string getDescription() const override {
        return "House Blend Coffee";
    }
    
    double cost() const override {
        return 0.89;
    }
};
// 抽象装饰器
class CondimentDecorator : public Beverage {
protected:
    std::shared_ptr<Beverage> beverage;
    
public:
    CondimentDecorator(std::shared_ptr<Beverage> b) : beverage(std::move(b)) {}
};
// 具体装饰器
class Mocha : public CondimentDecorator {
private:
    int shots;
    
public:
    Mocha(std::shared_ptr<Beverage> b, int s = 1) 
        : CondimentDecorator(std::move(b)), shots(s) {}
    
    std::string getDescription() const override {
        return beverage->getDescription() + ", Mocha(" + std::to_string(shots) + ")";
    }
    
    double cost() const override {
        return beverage->cost() + 0.20 * shots;
    }
};
class Whip : public CondimentDecorator {
public:
    using CondimentDecorator::CondimentDecorator;
    
    std::string getDescription() const override {
        return beverage->getDescription() + ", Whip";
    }
    
    double cost() const override {
        return beverage->cost() + 0.10;
    }
};
// 装饰器工厂
enum class Condiment {
    MOCHA, WHIP
};
class BeverageFactory {
public:
    static std::shared_ptr<Beverage> addCondiment(
        std::shared_ptr<Beverage> bev, 
        Condiment condiment,
        int param = 1
    ) {
        switch(condiment) {
            case Condiment::MOCHA:
                return std::make_shared<Mocha>(bev, param);
            case Condiment::WHIP:
                return std::make_shared<Whip>(bev);
            default:
                return bev;
        }
    }
};
// 构建器模式
class BeverageBuilder {
private:
    std::shared_ptr<Beverage> beverage;
    
public:
    explicit BeverageBuilder(std::shared_ptr<Beverage> base) : beverage(std::move(base)) {}
    
    BeverageBuilder& addMocha(int shots = 1) {
        beverage = std::make_shared<Mocha>(beverage, shots);
        return *this;
    }
    
    BeverageBuilder& addWhip() {
        beverage = std::make_shared<Whip>(beverage);
        return *this;
    }
    
    std::shared_ptr<Beverage> build() {
        return beverage;
    }
};
int main() {
    // 使用工厂模式
    auto beverage1 = std::make_shared<Espresso>();
    beverage1 = BeverageFactory::addCondiment(beverage1, Condiment::MOCHA, 2);
    beverage1 = BeverageFactory::addCondiment(beverage1, Condiment::WHIP);
    
    std::cout << beverage1->getDescription() 
              << " $" << beverage1->cost() << std::endl;
    
    // 使用构建器模式
    auto beverage2 = BeverageBuilder(std::make_shared<HouseBlend>())
                    .addMocha()
                    .addWhip()
                    .addMocha()
                    .build();
    
    std::cout << beverage2->getDescription() 
              << " $" << beverage2->cost() << std::endl;
    
    return 0;
}优化措施
- 通过工厂模式封装具体装饰器
            
            
              cpp
              
              
            
          
          // 1. 创建装饰器工厂
enum class Condiment {
    MOCHA, WHIP
};
class BeverageFactory {
public:
    static std::unique_ptr<Beverage> addCondiment(
        std::unique_ptr<Beverage> bev, 
        Condiment condiment
    ) {
        switch(condiment) {
            case Condiment::MOCHA:
                return std::make_unique<Mocha>(std::move(bev));
            case Condiment::WHIP:
                return std::make_unique<Whip>(std::move(bev));
            default:
                return bev;
        }
    }
};- 使用引用计数智能指针:
            
            
              cpp
              
              
            
          
          using BeveragePtr = std::shared_ptr<Beverage>;
// 避免所有权转移问题,允许多处引用同一对象- 构建器模式支持批量添加装饰器:
            
            
              cpp
              
              
            
          
          // 构建器模式示例
class BeverageBuilder {
private:
    BeveragePtr beverage;
    
public:
    explicit BeverageBuilder(BeveragePtr base) : beverage(std::move(base)) {}
    
    BeverageBuilder& addMocha() {
        beverage = std::make_shared<Mocha>(beverage);
        return *this;
    }
    
    BeverageBuilder& addWhip() {
        beverage = std::make_shared<Whip>(beverage);
        return *this;
    }
    
    BeveragePtr build() {
        return beverage;
    }
};
// 使用方式
auto beverage = BeverageBuilder(std::make_shared<Espresso>())
               .addMocha()
               .addWhip()
               .build();- 添加装饰器链验证:
            
            
              cpp
              
              
            
          
          // 在装饰器构造函数中添加验证逻辑
Mocha(std::unique_ptr<Beverage> b) : CondimentDecorator(std::move(b)) {
    // 可以添加对b类型的检查,防止非法组合
}- 参数化装饰器:
            
            
              cpp
              
              
            
          
          class Mocha : public CondimentDecorator {
private:
    int shots; // 支持多份摩卡
    
public:
    Mocha(std::unique_ptr<Beverage> b, int s = 1) 
        : CondimentDecorator(std::move(b)), shots(s) {}
    
    double cost() const override {
        return beverage->cost() + 0.20 * shots;
    }
};- 动态移除装饰器(需要扩展接口):
            
            
              cpp
              
              
            
          
          class RemovableCondiment : public CondimentDecorator {
public:
    using CondimentDecorator::CondimentDecorator;
    virtual std::unique_ptr<Beverage> remove() = 0;
};附录
std::shared_ptr 与 std::unique_ptr的区别
1. std::make_unique(std::move(beverage))
- 独占所有权 :unique_ptr表示独占所有权,同一对象只能有一个指针指向它
- 所有权转移 :必须使用std::move转移所有权,原指针将变为空
- 不可复制 :无法复制unique_ptr,只能移动
2. std::make_shared(beverage)
- 共享所有权 :shared_ptr使用引用计数,允许多个指针共享同一对象
- 自动内存管理 :当最后一个shared_ptr被销毁时,对象才被释放
- 可复制 :可以复制shared_ptr,引用计数增加
3. 关键区别对比
| 特性 | std::unique_ptr | std::shared_ptr | 
|---|---|---|
| 所有权 | 独占 | 共享 | 
| 转移方式 | 必须使用 std::move | 直接复制 | 
| 原指针状态 | 转移后变为空 | 仍然有效,引用计数增加 | 
| 内存释放时机 | 指针被销毁时 | 最后一个指针被销毁时 | 
| 线程安全性 | 无特殊同步 | 引用计数操作是线程安全的 | 
| 性能 | 轻量级,无引用计数开销 | 有引用计数开销 | 
| 适用场景 | 对象所有权清晰,避免共享 | 需要多个所有者或生命周期管理复杂的场景 | 
4. 示例对比
            
            
              cpp
              
              
            
          
          // 使用unique_ptr(原示例)
auto beverage = std::make_unique<Espresso>();
beverage = std::make_unique<Mocha>(std::move(beverage)); // 原指针失效
// 使用shared_ptr
auto beverage = std::make_shared<Espresso>();
auto whippedBeverage = std::make_shared<Whip>(beverage); // 原指针仍有效委托链
1、内存布局与对象关系
通过内存布局和函数调用栈来理解。执行以下代码:
            
            
              cpp
              
              
            
          
          auto beverage = std::make_shared<Espresso>(); // 步骤1
beverage = std::make_shared<Whip>(beverage);  // 步骤2
beverage = std::make_shared<Mocha>(beverage); // 步骤3- 
步骤1后的内存状态 栈内存 堆内存 
 +--------+ +-------------+
 | beverage|---------->| Espresso对象 |
 +--------+ +-------------+
- 
2.步骤2后的内存状态 栈内存 堆内存 
 +--------+ +-------------+
 | beverage|---------->| Whip对象 |
 +--------+ +-------------+
 | beverage_ |
 +---------->| Espresso对象 |
 +-------------+
- 
步骤3后的内存状态 栈内存 堆内存 
 +--------+ +-------------+
 | beverage|---------->| Mocha对象 |
 +--------+ +-------------+
 | beverage_ |
 +---------->| Whip对象 |
 +-------------+
 | beverage_ |
 +---------->| Espresso对象 |
 +-------------+
2、函数调用的底层机制
当我们调用beverage->cost()时,实际发生的是:
- 
静态类型与动态类型: - beverage的静态类型是- Beverage*(基类指针)
- 动态类型是Mocha(运行时实际指向的对象类型)
 
- 
虚函数表(VTable): - 每个包含虚函数的类都有一个虚函数表
- Mocha的虚函数表中,- cost()条目指向- Mocha::cost()实现
 
- 
调用栈展开: 
            
            
              cpp
              
              
            
          
          // 调用 beverage->cost()
Mocha::cost() { // 动态类型是Mocha,通过VTable找到此函数
    return beverage_->cost() + 0.20; // beverage_指向Whip对象
    // ↑ 此处发生多态调用
}
// 执行 Whip::cost()
Whip::cost() { // 通过Whip对象的VTable找到此函数
    return beverage_->cost() + 0.10; // beverage_指向Espresso对象
    // ↑ 再次发生多态调用
}
// 执行 Espresso::cost()
Espresso::cost() { // 通过Espresso对象的VTable找到此函数
    return 1.99;
}3、与递归的本质区别
- 递归调用
            
            
              cpp
              
              
            
          
          // 错误示例:直接递归调用自身
Mocha::cost() {
    return cost() + 0.20; // 无限递归,栈溢出
}- 装饰器模式的委托调用
            
            
              cpp
              
              
            
          
          // 正确实现:通过基类接口调用
Mocha::cost() {
    return beverage_->cost() + 0.20; // 调用的是另一个对象的cost()
}4、验证装饰器链
            
            
              cpp
              
              
            
          
          auto base = std::make_shared<Espresso>();
std::cout << "Base addr: " << base.get() << "\n";
auto withWhip = std::make_shared<Whip>(base);
std::cout << "Whip addr: " << withWhip.get() << "\n";
std::cout << "Whip's inner addr: " << withWhip->getInnerAddr() << "\n";
// 在Whip类中添加辅助方法
class Whip : public CondimentDecorator {
public:
    Beverage* getInnerAddr() const { return beverage_.get(); }
};输出结果类似:
Base addr: 0x12345678
Whip addr: 0x87654321
Whip's inner addr: 0x12345678  // 与base地址相同5、性能
- 调用开销
- 每次装饰器调用涉及一次虚函数调用(通常1-2个指令周期)
- 多层装饰会增加调用链长度,但现代CPU对此类优化较好
- 内存布局
- 每个装饰器增加一个指针大小的内存开销
- 对象在堆上非连续分配,可能影响缓存命中率