设计模式之装饰模式

1 核心定义

装饰模式是一种结构型设计模式,它允许在不改变现有对象结构的情况下,动态地给该对象添加一些额外的职责。装饰模式通过创建包装对象(装饰器)来包裹真实对象,提供了比继承更有弹性的替代方案。

2 核心思想

继承的替代方案:提供比继承更灵活的扩展方式

开闭原则:对扩展开放,对修改封闭

动态性:可以在运行时动态添加或撤销功能

透明性:装饰对象和真实对象具有相同的接口

3 结构组成

Component(组件接口):定义对象接口,可动态添加职责

ConcreteComponent(具体组件):实现组件接口的具体对象

Decorator(装饰器抽象类):持有一个组件引用,实现组件接口

ConcreteDecorator(具体装饰器):向组件添加具体职责

4 应用场景举例

以咖啡订单系统为例,假设我们需要计算不同咖啡饮品的价格和描述:

基础咖啡:浓缩咖啡、美式咖啡

配料:牛奶、摩卡、奶油等

顾客可以自由组合基础咖啡和配料

5 UML

6 c++ 代码实现

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

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

// 具体组件:浓缩咖啡
class Espresso : public Coffee {
public:
    string getDescription() const override {
        return "浓缩咖啡";
    }
    
    double cost() const override {
        return 25.0;  // 基础价格25元
    }
};

// 具体组件:美式咖啡
class Americano : public Coffee {
public:
    string getDescription() const override {
        return "美式咖啡";
    }
    
    double cost() const override {
        return 20.0;  // 基础价格20元
    }
};

// 抽象装饰器:配料装饰器
class CoffeeDecorator : public Coffee {
protected:
    Coffee* coffee;  // 持有的被装饰对象
    
public:
    CoffeeDecorator(Coffee* c) : coffee(c) {}
    
    string getDescription() const override {
        return coffee->getDescription();
    }
    
    double cost() const override {
        return coffee->cost();
    }
    
    virtual ~CoffeeDecorator() {
        delete coffee;
    }
};

// 具体装饰器:牛奶
class Milk : public CoffeeDecorator {
public:
    Milk(Coffee* c) : CoffeeDecorator(c) {}
    
    string getDescription() const override {
        return coffee->getDescription() + " + 牛奶";
    }
    
    double cost() const override {
        return coffee->cost() + 5.0;  // 牛奶加5元
    }
};

// 具体装饰器:摩卡
class Mocha : public CoffeeDecorator {
public:
    Mocha(Coffee* c) : CoffeeDecorator(c) {}
    
    string getDescription() const override {
        return coffee->getDescription() + " + 摩卡";
    }
    
    double cost() const override {
        return coffee->cost() + 8.0;  // 摩卡加8元
    }
};

// 具体装饰器:奶油
class Whip : public CoffeeDecorator {
public:
    Whip(Coffee* c) : CoffeeDecorator(c) {}
    
    string getDescription() const override {
        return coffee->getDescription() + " + 奶油";
    }
    
    double cost() const override {
        return coffee->cost() + 6.0;  // 奶油加6元
    }
};

// 客户端代码
int main() {
    // 点一杯浓缩咖啡加牛奶和摩卡
    Coffee* coffee = new Espresso();
    coffee = new Milk(coffee);
    coffee = new Mocha(coffee);
    
    cout << "订单: " << coffee->getDescription() << endl;
    cout << "价格: " << coffee->cost() << "元" << endl;
    
    delete coffee;
    return 0;
}

7 总结

如果不使用装饰模式,可能会有以下几种实现方式

方式1:使用继承(每个组合创建一个子类)

cpp 复制代码
// 问题:类爆炸
class EspressoWithMilk : public Espresso { /* ... */ };
class EspressoWithMocha : public Espresso { /* ... */ };
class EspressoWithMilkAndMocha : public Espresso { /* ... */ };
class AmericanoWithMilk : public Americano { /* ... */ };
// ... 需要为每种组合创建类,如果有3种基础咖啡和5种配料,就需要3*2^5=96个类!

方式2:使用属性标志

cpp 复制代码
class Coffee {
private:
    bool hasMilk;
    bool hasMocha;
    bool hasWhip;
    // ... 需要为每种配料添加标志
    
public:
    double cost() {
        double total = basePrice;
        if (hasMilk) total += 5;
        if (hasMocha) total += 8;
        if (hasWhip) total += 6;
        return total;
    }
};

主要问题:

1 违反开闭原则:每次添加新的配料都需要修改Coffee类

2 代码臃肿:类的职责过重,包含所有配料的逻辑

3 维护困难:添加新配料需要修改现有代码,容易引入bug

4 灵活性差:无法动态改变配料组合,配料的组合在编译时就固定了

5 代码重复:如果配料的价格计算逻辑复杂,不同咖啡类中会有重复代码

装饰模式的优势

1 灵活扩展:可以在不修改现有代码的情况下添加新功能

2 组合自由:可以在运行时自由组合不同的装饰器

3 符合单一职责:每个装饰器只负责一个特定的功能

4 避免类爆炸:不需要为每个组合创建新的子类

5 可撤销:可以动态地添加或移除装饰器

装饰模式通过将复杂的功能分解为简单的小功能,并通过组合的方式实现复杂功能,是一种非常灵活的代码组织方式。

相关推荐
TimberWill2 小时前
优化if else过多的方案(含设计模式处理方式)
java·设计模式
@insist1232 小时前
软件设计师-结构型与行为型设计模式全解:软考设计模式考点一站式通关
设计模式·软考·软件设计师·软件水平考试
JTCC5 小时前
Java 设计模式西游篇 - 第五回:装饰者模式添法力 悟空披挂新战袍
java·观察者模式·设计模式
逆境不可逃5 小时前
【从零入门23种设计模式13】行为型之责任链模式
算法·leetcode·游戏·设计模式·责任链模式
Thomas.Sir6 小时前
SpringMVC 工作原理深入解析
spring·设计模式·mvc·spring mvc
逆境不可逃7 小时前
【从零入门23种设计模式15】行为型之解释器模式
设计模式·解释器模式
geovindu7 小时前
python: Adapter Pattern
java·python·设计模式·适配器模式
蜜獾云7 小时前
设计模式之工厂方法模式(5):稍微复杂一点的工厂模式
java·设计模式·工厂方法模式
Voyager_47 小时前
吃透设计模式:从原理到落地(如何选型),Java/Spring开发场景
java·spring·设计模式