定义
动态地给一个对象增加一些额外的职责。就增加功能而言,装饰器模式比生成子类更为灵活。 ——
《设计模式》GoF
背景
普通员工有销售奖金,累计奖金,部⻔经理除此之外还有团队奖金;后面可能会添加环比增⻓奖金,同时可能针对不同的职位产生不同的奖金组合;
代码decorator1
// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合; // 销售奖金 = 当月销售额 * 4% // 累计奖金 = 总的回款额 * 0.2% // 部门奖金 = 团队销售额 * 1% // 环比奖金 = (当月销售额-上月销售额) * 1% // 销售后面的参数可能会调整 class Context { public: bool isMgr; // User user; // 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); }
代码decorator2
// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合; // 销售奖金 = 当月销售额 * 4% // 累计奖金 = 总的回款额 * 0.2% // 部门奖金 = 团队销售额 * 1% // 环比奖金 = (当月销售额-上月销售额) * 1% // 销售后面的参数可能会调整 class Context { public: bool isMgr; // User user; // double groupsale; }; // 试着从职责出发,将职责抽象出来 class CalcBonus { public: CalcBonus(CalcBonus * c = nullptr) {} virtual double Calc(Context &ctx) { return 0.0; // 基本工资 } virtual ~CalcBonus() {} protected: CalcBonus* cc; }; class CalcMonthBonus : public CalcBonus { public: CalcMonthBonus(CalcBonus * c) : cc(c) {} virtual double Calc(Context &ctx) { double mbonus /*= 计算流程忽略*/; return mbonus + cc->Calc(ctx); } }; class CalcSumBonus : public CalcBonus { public: CalcSumBonus(CalcBonus * c) : cc(c) {} virtual double Calc(Context &ctx) { double sbonus /*= 计算流程忽略*/; return sbonus + cc->Calc(ctx); } }; class CalcGroupBonus : public CalcBonus { public: CalcGroupBonus(CalcBonus * c) : cc(c) {} virtual double Calc(Context &ctx) { double gbnonus /*= 计算流程忽略*/; return gbnonus + cc->Calc(ctx); } }; class CalcCycleBonus : public CalcBonus { public: CalcGroupBonus(CalcBonus * c) : cc(c) {} virtual double Calc(Context &ctx) { double gbnonus /*= 计算流程忽略*/; return gbnonus + cc->Calc(ctx); } }; int main() { // 1. 普通员工 Context ctx1; CalcBonus *base = new CalcBonus(); CalcBonus *cb1 = new CalcMonthBonus(base); CalcBonus *cb2 = new CalcSumBonus(cb1); cb2->Calc(ctx1); // 2. 部门经理 Context ctx2; CalcBonus *cb3 = new CalcGroupBonus(cb2); cb3->Calc(ctx2); }
要点
- 通过采用组合而非继承的手法, 装饰器模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
- 不是解决“多子类衍生的多继承”问题,而是解决“父类在多个方向上的扩展功能”问题;
- 装饰器模式把一系列复杂的功能分散到每个装饰器当中,一般一个装饰器只实现一个功能,实现复用装饰器的功能;
本质
- 动态组合;
结构图