设计模式---装饰模式

78 阅读4分钟

装饰模式的定义

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地为对象添加功能,而无需改变其类。装饰模式通过创建一系列装饰类,这些类实现与被装饰对象相同的接口,并在内部保存一个被装饰对象的引用,从而动态地增加对象的行为。

特点

  • 动态扩展对象的功能:相比于通过继承扩展对象功能,装饰模式可以动态地添加或删除功能。

  • 灵活组合功能:多个装饰器可以组合使用,以多层次的方式为对象添加功能。

  • 遵循开放-封闭原则:装饰模式允许在不修改现有代码的情况下扩展系统功能。

代码示例

1. 基本接口和实现

  • ISale 接口:定义了一个基本的收费方法 acceptCash
  • CashNormal 类:实现了 ISale 接口,提供了基本的收费算法,即原价收费。
class ISale 
{
public:
    virtual double acceptCash(double, int) = 0;
    virtual ~ISale() {}
};

class CashNormal :public ISale
{
public:
    double acceptCash(double price, int num) override
    {
        return price * num;
    }
};

2. 装饰器基类

  • CashSuper 类:继承自 ISale 接口,并持有一个指向 ISale 对象的指针 m_component。它提供了一个 decorate 方法,用于设置被装饰的组件,以及一个 acceptCash 方法,用于调用被装饰对象的方法。
class CashSuper :public ISale
{
public:
    void decorate(ISale* component)
    {
        m_component = component;
    }

    double acceptCash(double price, int num) override
    {
        double result = 0;
        if (m_component != nullptr)
        {
            result = m_component->acceptCash(price, num);
        }
        return result;
    }

protected:
    ISale* m_component = nullptr;
};

3. 具体装饰器

  • CashRebate 类:继承自 CashSuper,实现了打折算法。在 acceptCash 方法中,先计算打折后的价格,然后调用基类的 acceptCash 方法。
class CashRebate :public CashSuper
{
public:
    CashRebate(double rebate)
    {
        m_rebate = rebate;
    }

    double acceptCash(double price, int num) override
    {
        double result = price * num * m_rebate;
        return CashSuper::acceptCash(result, 1);
    }

private:
    double m_rebate = 1;
};
  • CashReturn 类:继承自 CashSuper,实现了满减算法。在 acceptCash 方法中,先计算满减后的价格,然后调用基类的 acceptCash 方法。

4. 上下文类

  • CashContext 类:根据不同的收费类型,动态组合各种收费算法。
class CashContext
{
public:
    CashContext(int type)
    {
        switch (type)
        {
        case 1:
            cs = new CashNormal();
            break;
        case 2:
            cs = new CashRebate(0.8);
            break;
        case 3:
            cs = new CashRebate(0.7);
            break;
        case 4:
            cs = new CashReturn(300, 100);
            break;
        case 5:
        {
            CashNormal* cn = new CashNormal();
            CashReturn* cr1 = new CashReturn(300, 100);
            CashRebate* cr2 = new CashRebate(0.8);

            cr1->decorate(cn);   // 用满300返100算法包装原价算法
            cr2->decorate(cr1);  // 用打8折算法包装满300返100算法
            cs = cr2;            // 将包装好的算法组合传递给cs对象
            break;
        }
        case 6:
        {
            CashNormal* cn2 = new CashNormal();
            CashRebate* cr3 = new CashRebate(0.7);
            CashReturn* cr4 = new CashReturn(200, 50);

            cr3->decorate(cn2);
            cr4->decorate(cr3);
            cs = cr4;
            break;
        }
        }
    }

    double getResult(double price, int num)
    {
        return cs->acceptCash(price, num);
    }

private:
    ISale* cs;
};

5. 客户端代码

main 函数中,通过 CashContext 创建具体的收费策略,并计算最终的收费金额。

int main()
{
    CashContext* cc = new CashContext(5);
    double result = cc->getResult(84.8, 35);

    std::cout << "收费金额:" << result << std::endl;

    delete cc;
    system("pause");
    return 0;
}

6.解释

  • ISale 接口: 定义了收费的标准方法 acceptCash,所有收费策略类都要实现这个接口。

  • CashSuper 类: 是一个抽象类,用于装饰具体的收费策略类。它持有一个指向 ISale 对象的指针 m_component,可以调用被装饰对象的 acceptCash 方法。

  • 具体收费策略类(CashNormal、CashRebate、CashReturn) :

    • CashNormal 实现了正常收费的逻辑。
    • CashRebate 实现了打折收费的逻辑,通过调用 CashSuper::acceptCash 来继续处理被装饰对象的逻辑。
    • CashReturn 实现了满减收费的逻辑,通过调用 CashSuper::acceptCash 来继续处理被装饰对象的逻辑。
  • CashContext 类: 根据传入的类型选择具体的收费策略,并通过组合不同的装饰器实现复杂的收费逻辑。它将最终的收费策略保存在 cs 指针中,通过 getResult 方法返回最终的收费结果。

总结

装饰模式的实现体现在 CashSuper 及其子类的设计中。通过 decorate 方法,可以将一个 ISale 对象传递给另一个 ISale 对象,从而实现动态组合不同的收费策略。

在装饰模式中,调用基类的 acceptCash 方法的目的是为了保持装饰器链的完整性和灵活性。通过调用基类的方法,可以确保装饰器的每一层都能正确地执行其功能,同时将调用传递给下一个装饰器或被装饰对象。这种方式可以递归地应用多个装饰器,使得功能的组合更加灵活和可扩展。