目录
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许通过将对象包装在装饰器类的实例中来动态地添加新的行为和责任。这种模式可以在不修改现有代码的情况下,灵活地扩展对象的功能。
考虑一个咖啡店的场景,有不同种类的咖啡,你可以选择添加不同的配料,比如牛奶、糖和巧克力。使用装饰模式可以动态地为咖啡添加不同的配料,而不需要修改咖啡类的代码。
- #include
- #include
-
- // Step 1: Component(组件)
- class Coffee {
- public:
- virtual std::string getDescription() const = 0;
- virtual double cost() const = 0;
- virtual ~Coffee() = default;
- };
-
- // Step 2: ConcreteComponent(具体组件)
- class SimpleCoffee : public Coffee {
- public:
- std::string getDescription() const override {
- return "简单咖啡";
- }
-
- double cost() const override {
- return 5.0;
- }
- };
-
- // Step 3: Decorator(装饰器)
- class CoffeeDecorator : public Coffee {
- protected:
- Coffee* coffee;
-
- public:
- CoffeeDecorator(Coffee* c) : coffee(c) {}
-
- std::string getDescription() const override {
- return coffee->getDescription();
- }
-
- double cost() const override {
- return coffee->cost();
- }
- virtual ~CoffeeDecorator() {
- if(coffee)
- {
- delete coffee;
- }
- }
- };
-
- // Step 4: ConcreteDecorator(具体装饰器)
- class MilkDecorator : public CoffeeDecorator {
- public:
- MilkDecorator(Coffee* c) : CoffeeDecorator(c) {}
-
- std::string getDescription() const override {
- return coffee->getDescription() + ",加牛奶";
- }
-
- double cost() const override {
- return coffee->cost() + 2.0;
- }
- ~MilkDecorator () override {}
- };
-
- class SugarDecorator : public CoffeeDecorator {
- public:
- SugarDecorator(Coffee* c) : CoffeeDecorator(c) {}
-
- std::string getDescription() const override {
- return coffee->getDescription() + ",加糖";
- }
-
- double cost() const override {
- return coffee->cost() + 1.0;
- }
- ~SugarDecorator () override {}
- };
-
- int main() {
- // 创建一个简单咖啡
- Coffee* myCoffee = new SimpleCoffee();
- std::cout << "描述:" << myCoffee->getDescription() << ",价格:" << myCoffee->cost() << "元" << std::endl;
-
- // 使用装饰器动态添加牛奶
- myCoffee = new MilkDecorator(myCoffee);
- std::cout << "描述:" << myCoffee->getDescription() << ",价格:" << myCoffee->cost() << "元" << std::endl;
-
- // 使用装饰器再添加糖
- myCoffee = new SugarDecorator(myCoffee);
- std::cout << "描述:" << myCoffee->getDescription() << ",价格:" << myCoffee->cost() << "元" << std::endl;
-
- // 释放内存
- delete myCoffee;
-
- return 0;
- }
在这个示例中,Coffee是组件,SimpleCoffee是具体组件,CoffeeDecorator是装饰器,MilkDecorator和SugarDecorator是具体装饰器。通过不同的组合,我们可以动态地扩展咖啡的描述和价格,而无需修改原始的咖啡类。这就是装饰模式
还是咖啡店的例子。咖啡店提供多种咖啡,如浓缩咖啡(Espresso)、拿铁(Latte)、美式咖啡(Americano)等。顾客可以根据自己的口味选择添加不同的配料,如奶泡(Foam)、糖浆(Syrup)、焦糖(Caramel)等。
如果不使用装饰模式,而是为每一种可能的咖啡组合创建一个新的类,那么需要的子类个数将是咖啡类型个数和装饰类型个数的乘积。这是因为对于每一种咖啡,都可以添加或不添加每一种装饰,所以组合的数量是两者之积。
假设咖啡类型个数是 m,装饰的类型个数是 n,那么需要的类个数将是 m * n+m+1。(不考虑多个组合)
例如,如果有 3 种咖啡(Espresso, Latte, Americano)和 3 种装饰(Foam, Syrup, Caramel),那么需要的子类个数将是 3 * 3 = 9 个,即:
如果考虑同时添加多种装饰的组合,那么子类的个数将会是指数级的增长,而不是简单的乘积关系。这是因为每种咖啡都可以独立地添加或不添加每种装饰,而且装饰之间也可以相互组合。
假设有 m 种咖啡和 n 种装饰,每种咖啡都可以添加或不添加每种装饰,那么对于每种咖啡,都有 2^n 种可能的装饰组合(包括不添加任何装饰的情况)。因此,对于 m 种咖啡,总的类个数将是 m * 2^n+m+1。
例如,如果有 3 种咖啡(Espresso, Latte, Americano)和 3 种装饰(Foam, Syrup, Caramel),那么对于每种咖啡,都有 2^3 = 8 种可能的装饰组合。因此,总的子类个数将是 3 * 8 = 24 个。这还不包括那些可能同时添加多种装饰并且咖啡种类也不同的组合。
从上述所叙可以看出这样做会导致类的数量迅速增加,而且很难扩展和维护。
- class Coffee {
- public:
- virtual void prepare() = 0;
- };
-
- class Espresso : public Coffee {
- public:
- void prepare() override {
- // Prepare espresso
- }
- };
-
- class EspressoWithFoam : public Espresso {
- public:
- void prepare() override {
- // Prepare with foam
- }
- };
-
- // ...其他咖啡类
使用装饰模式,我们可以创建一个Coffee接口和多个实现该接口的类(如Espresso、Latte、Americano),然后创建一个CoffeeDecorator类,它实现了Coffee接口并持有一个Coffee对象。这样,我们可以创建多个CoffeeDecorator子类来添加不同的配料。
假设咖啡类型个数是 m,装饰的类型个数是 n,那么需要的类个数将是 m+ n+1(装饰子类)+1(基类)。
例如,如果有 3 种咖啡(Espresso, Latte, Americano)和 3 种装饰(Foam, Syrup, Caramel),我们需要创建的类个数是1+3+3+1=8个,相对于上面不使用装饰模式的示例,使用了装饰模式后,常见的类个数少了很多。
- class Coffee {
- public:
- virtual void prepare() = 0;
- virtual ~Coffee() = default;
- };
-
- class Espresso : public Coffee {
- public:
- void prepare() override {
- // Prepare espresso
- }
- };
-
- class CoffeeDecorator : public Coffee {
- protected:
- Coffee* decoratedCoffee;
- public:
- CoffeeDecorator(Coffee* coffee) : decoratedCoffee(coffee) {}
- void prepare() override {
- decoratedCoffee->prepare();
- }
- virtual ~CoffeeDecorator() {
- delete decoratedCoffee;
- }
-
- };
-
- class FoamDecorator : public CoffeeDecorator {
- public:
- FoamDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
- void prepare() override {
- // Add foam
- decoratedCoffee->prepare();
- }
- ~FoamDecorator () override {}
- };
-
- class SyrupDecorator : public CoffeeDecorator {
- public:
- SyrupDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
- void prepare() override {
- // Add syrup
- decoratedCoffee->prepare();
- }
- ~SyrupDecorator () override {}
- };
-
- // 使用示例
- int main() {
- Coffee* myCoffee = new Espresso();
- myCoffee = new FoamDecorator(myCoffee);
- myCoffee = new SyrupDecorator(myCoffee);
- myCoffee->prepare(); // Prepares espresso with foam and syrup
- delete myCoffee; // Deletes the entire decoration chain
- return 0;
- }
这里实现了灵活添加组件
- Coffee* myCoffee = new Espresso();
- myCoffee = new FoamDecorator(myCoffee);
- myCoffee = new SyrupDecorator(myCoffee);
避免使用继承导致的类爆炸
通过组件通过添加装饰类的方式,将装饰功能分离,而不需要使用继承或者组合的方式,解耦了功能的扩展,新的装饰添加时,不需要改动组件,只要添加新的装饰类即可,避免了需要不断继承带来的风险和大量继承类。保持类的简单性和单一责任原则
不使用装饰模式

使用装饰模式

当需要动态地为一个对象添加额外的功能,而且希望这些功能可以灵活组合时,装饰模式是一个很好的选择。这样可以避免使用大量子类来实现所有可能的组合,而是使用装饰器来动态地添加这些功能。
经常会发现在类的层次结构中添加新功能导致的子类爆炸问题。装饰模式通过将功能分离到单独的装饰器类中,避免了这种情况的发生。
使用装饰模式可以将一些复杂的功能分离到单独的装饰器类中,使得原始类保持简单和具有单一职责。
装饰模式允许在运行时动态地添加或删除对象的功能,这对于某些情况下的配置和扩展非常有用。
Java中的输入输出流就是一个典型的装饰器模式的例子。基本的InputStream或OutputStream可以通过添加额外的功能,比如缓冲、加密或压缩等,而无需修改它们的代码。
在GUI编程中,经常需要动态地添加新的功能或外观到用户界面组件上。比如,一个简单的文本框可以通过装饰模式来添加滚动条、边框、背景色等功能,而无需修改原始文本框类的代码。
在Web开发中,过滤器常常用于对请求或响应进行处理,比如身份验证、日志记录、数据压缩等。使用装饰模式可以轻松地添加新的过滤功能,同时保持代码的灵活性和可维护性。