装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在不改变原有对象结构的情况下,动态地给对象添加额外的职责(功能)。这种模式通过创建一个包装对象(装饰器)来包裹真实对象,从而在保持接口一致的前提下扩展功能。

装饰器模式的核心角色

  1. 抽象组件(Component):定义对象的接口,可以给这些对象动态添加职责
  2. 具体组件(Concrete Component):实现抽象组件接口,是被装饰的原始对象
  3. 抽象装饰器(Decorator):继承或实现抽象组件,包含一个指向抽象组件的引用
  4. 具体装饰器(Concrete Decorator):实现抽象装饰器,给组件添加具体的额外功能

装饰器模式的实现示例

下面以"咖啡订单"为例展示装饰器模式的实现,我们可以给基础咖啡动态添加各种配料(如牛奶、糖、巧克力等):

#include <iostream>
#include <string>

// 抽象组件:咖啡
class Coffee {
public:
    virtual std::string getDescription() const = 0;
    virtual double cost() const = 0;
    virtual ~Coffee() = default;
};

// 具体组件:基础咖啡
class Espresso : public Coffee {
public:
    std::string getDescription() const override {
        return "浓缩咖啡";
    }

    double cost() const override {
        return 25.0;
    }
};

class Americano : public Coffee {
public:
    std::string getDescription() const override {
        return "美式咖啡";
    }

    double cost() const override {
        return 20.0;
    }
};

// 抽象装饰器:配料装饰器
class CondimentDecorator : public Coffee {
protected:
    Coffee* coffee;  // 被装饰的咖啡

public:
    CondimentDecorator(Coffee* coffee) : coffee(coffee) {}
    
    ~CondimentDecorator() override {
        delete coffee;  // 释放被装饰的对象
    }
};

// 具体装饰器:牛奶
class Milk : public CondimentDecorator {
public:
    Milk(Coffee* coffee) : CondimentDecorator(coffee) {}

    std::string getDescription() const override {
        return coffee->getDescription() + ", 牛奶";
    }

    double cost() const override {
        return coffee->cost() + 5.0;  // 牛奶加价5元
    }
};

// 具体装饰器:糖
class Sugar : public CondimentDecorator {
public:
    Sugar(Coffee* coffee) : CondimentDecorator(coffee) {}

    std::string getDescription() const override {
        return coffee->getDescription() + ", 糖";
    }

    double cost() const override {
        return coffee->cost() + 2.0;  // 糖加价2元
    }
};

// 具体装饰器:巧克力
class Chocolate : public CondimentDecorator {
public:
    Chocolate(Coffee* coffee) : CondimentDecorator(coffee) {}

    std::string getDescription() const override {
        return coffee->getDescription() + ", 巧克力";
    }

    double cost() const override {
        return coffee->cost() + 8.0;  // 巧克力加价8元
    }
};

// 客户端使用
int main() {
    // 点一杯纯浓缩咖啡
    Coffee* espresso = new Espresso();
    std::cout << espresso->getDescription() << " 价格: " << espresso->cost() << "元" << std::endl;
    delete espresso;

    // 点一杯加牛奶和糖的美式咖啡
    Coffee* coffee = new Sugar(new Milk(new Americano()));
    std::cout << coffee->getDescription() << " 价格: " << coffee->cost() << "元" << std::endl;
    delete coffee;

    // 点一杯加双份巧克力的浓缩咖啡
    Coffee* fancyCoffee = new Chocolate(new Chocolate(new Espresso()));
    std::cout << fancyCoffee->getDescription() << " 价格: " << fancyCoffee->cost() << "元" << std::endl;
    delete fancyCoffee;

    return 0;
}

装饰器模式的工作原理

  1. 装饰器与被装饰对象实现相同的接口(或继承相同的抽象类)
  2. 装饰器包含一个指向被装饰对象的引用
  3. 装饰器在调用被装饰对象的方法前后,可以添加额外的行为
  4. 可以通过嵌套多个装饰器,实现多种功能的组合

装饰器模式与继承的对比

  • 继承:在编译时静态添加功能,不够灵活,可能导致类爆炸
  • 装饰器:在运行时动态添加功能,更灵活,可以组合多个功能

装饰器模式的应用场景

  1. 需要动态地给对象添加或移除功能
  2. 需要给对象添加多个可组合的功能
  3. 不适合使用继承来扩展功能(如final类或类层次复杂)
  4. 希望在不影响其他对象的情况下,为单个对象添加功能

装饰器模式的优缺点

优点

  • 可以动态地给对象添加功能,灵活性高
  • 可以组合多个装饰器,实现功能的叠加
  • 遵循开放-封闭原则,扩展新功能无需修改原有代码
  • 避免了使用继承带来的类爆炸问题

缺点

  • 增加了系统复杂度,需要理解多个装饰器的工作方式
  • 装饰器嵌套过多可能导致代码可读性下降
  • 对于多次装饰的对象,调试和排错可能比较困难

装饰器模式在C++标准库中也有应用,例如IO流库中的std::ifstreamstd::ofstream配合std::ios::binarystd::ios::app等标志,以及std::endl等操纵符,都是装饰器模式思想的体现。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐