设计模式——装饰器模式


一、基本知识

在我第一次接触装饰器模式的时候,将它和责任链模式分不开,但是事实上它们是有区别的。

1.定义

动态地给一个对象增加一些额外的职责。就增加功能而言,装饰器模式比生产子类更为灵活。

2.背景示例

普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能针对不同的职位产生不同的奖金组合。
在这里插入图片描述

3.要点

①通过采用组合而非继承的手法, 装饰器模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
②不是解决“多子类衍生问题”问题,而是解决“父类在多个方向上的扩展功能”问题。
③装饰器模式把一系列复杂的功能分散到每个装饰器当中,一般一个装饰器只实现一个功能,实现复用装饰器的功能。

二、结构图

在这里插入图片描述
对于本人来说,先看结构图的话,看不懂,都是先看示例代码,回头来再看结构图才看得懂。

三、实例说明

1.普通代码

#include<iostream>
#include<string>
using namespace std;

// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 4%
// 累计奖金 = 总的回款额 * 2%
// 部门奖金 = 团队销售额 * 1%
// 环比奖金 = (当月销售额-上月销售额) * 1%
// 销售后面的参数可能会调整
class Context {
public:
    bool isMgr;
    string name;
    double groupsale;
};

class Bonus {
public:
    double CalcBonus(Context& ctx) {
        double bonus = 0.0;
        bonus += CalcMonthBonus(ctx);
        bonus += CalcSumBonus(ctx);
        if (ctx.isMgr) {
            bonus += CalcGroupBonus(ctx);
        }
        return bonus;
    }
private:
    double CalcMonthBonus(Context& ctx) {
        double bonus;
        /*一系列操作求销售奖金*/
        return bonus;
    }
    double CalcSumBonus(Context& ctx) {
        double bonus;
        /*一系列操作求总的回款额*/
        return bonus;
    }
    double CalcGroupBonus(Context& ctx) {
        double bonus;
        /*一系列操作求部门奖金*/
        return bonus;
    }
};

int main() {
    Context ctx;
    // 设置 ctx
    Bonus* bonus = new Bonus;
    bonus->CalcBonus(ctx);
}

2.装饰器模式代码

#include <iostream>
#include <string>
// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 4%
// 累计奖金 = 总的回款额 * 0.2%
// 部门奖金 = 团队销售额 * 1%
// 环比奖金 = (当月销售额-上月销售额) * 1%
// 销售后面的参数可能会调整
using namespace std;
class Context {
public:
    bool isMgr;
    string name;
    double groupsale;
};

// 试着从职责出发,将职责抽象出来
class CalcBonus {    
public:
    CalcBonus(CalcBonus * c = nullptr) : cc(c) {}
    virtual double Calc(Context &ctx) {
        return 0.0; // 基本工资
    }
    virtual ~CalcBonus() {}

protected:
    CalcBonus* cc;//通过组合的方式
};

class CalcMonthBonus : public CalcBonus {
public:
    CalcMonthBonus(CalcBonus * c) : CalcBonus(c) {}
    virtual double Calc(Context &ctx) {
        double mbonus;
        /*求销售奖金操作*/
        return mbonus + cc->Calc(ctx);
    }
};

class CalcSumBonus : public CalcBonus {
public:
    CalcSumBonus(CalcBonus * c) : CalcBonus(c) {}
    virtual double Calc(Context &ctx) {
        double sbonus;
        /*求累计奖金操作*/
        return sbonus + cc->Calc(ctx);
    }
};

class CalcGroupBonus : public CalcBonus {
public:
    CalcGroupBonus(CalcBonus * c) : CalcBonus(c) {}
    virtual double Calc(Context &ctx) {
        double gbnonus;
        /*求部门奖金操作*/
        return gbnonus + cc->Calc(ctx);
    }
};

class CalcCycleBonus : public CalcBonus {
public:
    CalcCycleBonus(CalcBonus * c) : CalcBonus(c) {}
    virtual double Calc(Context &ctx) {
        double gbnonus ;
        /*求环比奖金操作*/
        return gbnonus + cc->Calc(ctx);
    }
};

int main() {
    // 1. 普通员工
    Context ctx1;
    ctx1.isMgr = false;
    CalcBonus *base = new CalcBonus();
    CalcBonus *cb1 = new CalcSumBonus(base);
    CalcBonus *cb2 = new CalcMonthBonus(cb1);
    cb2->Calc(ctx1);//普通员工奖金只求销售奖金和部门奖金

    // 2. 部门经理
    Context ctx2;
    ctx2.isMgr = true;
    CalcBonus *cb3 = new CalcGroupBonus(cb2);
    cb3->Calc(ctx2);//部门奖金求销售、累计、部门奖金(环比奖金暂时没有设置)
}

可以看见,装饰器和责任链最大的区别是装饰器模式是最主要的是组合手法。而责任链是继承,然后每个子类的实例之间通过链表的方式低度耦合。装饰器之所以采用组合,是因为子类并不只是其中一个子类符合条件进行操作,而是多个子类都要操作,比如奖金,是通过多个子类求出来的。
而责任链模式通常是某个子类来解决这个事情,而链表是通常按照一定的先后顺序将子类低度耦合起来,有事件要处理时,通过链表遍历这个链表上的类,找到适合处理这个事情的类,这个往往是一个类就可以处理完。
当然,其实大部分可以用装饰器模式的很可能也可以用责任链模式,因为责任链模式只是往往一个事件只需要一个类处理,但是也可以是多个类都要处理,但是通过执行先后顺序将类用链表连接起来,就像是一个工厂的流水线一样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟小胖_H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值