装饰模式
定义
动态地给一个对象增加一些额外的职责。就增加功能而言,装饰器模式比生产子类更为灵活。—— 《设计模式》GoF
适用场景
普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能针对不同的职位产生不同的奖金组合;我们要通过装饰模式为不同职位的员工添加奖金。
结构图
未使用是设计模式代码
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);
}
这样的设计并没有使不同的人的奖金计算分离,并且需要在类中添加很多变量(如isMgr,isBoss等)非常麻烦。
使用装饰模式
class Context {
public:
bool isMgr;
// User user;
// 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;
CalcBonus *base = new CalcBonus();
CalcBonus *cb1 = new CalcSumBonus(base);
CalcBonus *cb2 = new CalcMonthBonus(cb1);
cb2->Calc(ctx1);
// 2. 部门经理
Context ctx2;
CalcBonus *cb3 = new CalcGroupBonus(cb2);
cb3->Calc(ctx2);
}
可见装饰模式把不同职位的奖金计算分离,正如其名,向装修一样,需要什么添加什么。