您的位置:首页 > 其它

设计模式之策略模式

2016-11-05 23:07 162 查看

设计模式之策略模式

1. 问题引入

案例描述:

对于不同职位的员工,年终奖的计算方式不尽相同,并且职位的类别在可见的未来还会增加。试设计一个合适的年终奖计算方式,来适应这一情况。

问题抽象:

在某个问题中,针对不同的情况,会有不同的算法。而且,解决该问题的算法,未来很有可能会改变。

不同的时候需要不同的算法,并且我们不想支持我们并不使用的算法。 ——— 《设计模式,GOF》

2.解决方案

方案一:分情况考虑问题。

枚举职位,针对不同的职位选择不同的年终奖计算方案。

代码如下:

#include <iostream>

using namespace std;

typedef float Bonus;   //年终奖的金额
class Context{}; //计算年终奖需要的上下文条件

//枚举职位
enum Position
{
CEO,            // 首席执行官
PROGRAMMER,     // 程序猿
PM,             // 产品经理
ACCOUNTANT      // 会计
};

Bonus CEOBonus(const Context& context)
{
Bonus bonus = 0.0;

cout << "CEO的年终奖计算方式." << endl;
//...

return bonus;
}
Bonus ProgrammerBonus(const Context& context)
{
Bonus bonus = 0.0;

cout << "程序猿的年终奖计算方式." << endl;
//...

return bonus;
}
Bonus PMBonus(const Context& context)
{
Bonus bonus = 0.0;

cout << "PM的年终奖计算方式." << endl;
//...

return bonus;
}
Bonus AccountantBonus(const Context& context)
{
Bonus bonus = 0.0;
cout << "会计的年终奖计算方式." << endl;
//...

return bonus;
}

//年终奖的计算策略
class BonusStategy
{
private:
Position m_pos; //职位
public:
BonusStategy(Position pos) : m_pos(pos) {}
//改变职位
void changePos(Position pos)
{
m_pos = pos;
}
//获取当前职位
Position position() const
{
return m_pos;
}
Bonus CalBonus() const
{
Bonus bonus = 0.0;
Context context;
//Context的处理...

switch (m_pos)
{
case CEO:
bonus = CEOBonus(context);
break;
case PROGRAMMER:
bonus = ProgrammerBonus(context);
break;
case PM:
bonus = PMBonus(context);
break;
case ACCOUNTANT:
bonus = AccountantBonus(context);
break;
}
return bonus;
}
};

int main()
{
BonusStategy bs(Position::PROGRAMMER);
Bonus bonus = bs.CalBonus();
return 0;
}


当职位种类数不变的情况下,这样的解决方案比较完美地解决了这个问题。其一是,我们可以将年终奖计算的函数放在一个文件下,将
BonusStategy
类放在另一个文件。当我们需要改变某个职位的年终奖计算方式时,只需修改计算函数,而不会影响
BonusStategy
类。 其二是,当我们需要改变一个人的职位时,只需要改变他的
Position
变量,不必考虑其他因素。 另外,这个方法比较通俗易懂,不涉及特别复杂的编程技巧,可读写较强。

但是,这个方案也有两点弊端。其一是,冗余代码过多。实际上每个
BonusStategy
对象在
CalBonus()
函数中,只会选择一个分支,而它却存储了其他不需要的分支,更加耗费CPU。其二是,随着公司的规模不断扩大,不断有新的职位产生。当增加新的职位时,我们不仅需要增加其对应的枚举类型字段和年终奖计算函数,而且需要在
BonusStategy::CalBonus()
中的
switch
语句增加对应的
case
语句。

如,当我们需要新增一个架构师(Architecte)时。我们需要先在
Position
类型中增加
ARCHITESTE
字段,然后,增加计算其年终奖的
Bonus ArchitecteBonus(const Context& context)
函数,最后需要在在
BonusStategy::CalBonus()
函数的
switch
语句中加入下列语句 :

case ARCHITESTE:
bonus = ArchitecteBonus(context);
break;


方案二: 策略模式(Strategy)所提供的解决方案

将算法封装起来,并提供相同的接口,针对不同的职位,提供不同的算法实现。

代码实现如下:

#include <iostream>

using namespace std;

typedef float Bonus;   //年终奖的金额
class Context {}; //计算年终奖需要的上下文条件

class BonusStategy
{
public:
virtual Bonus CalBonus
(const Context& context) const = 0;
};

class CEOBonus : public BonusStategy
{
public:
virtual Bonus CalBonus
(const Context& context) const
{
Bonus bonus = 0.0;

cout << "CEO的年终奖计算方式." << endl;
//...

return bonus;
}
};

class ProgrammerBonus : public BonusStategy
{
public:
virtual Bonus CalBonus
(const Context& context) const
{
Bonus bonus = 0.0;

cout << "程序猿的年终奖计算方式." << endl;
//...

return bonus;
}
};

class PMBonus : public BonusStategy
{
public:
virtual Bonus CalBonus
(const Context& context) const
{
Bonus bonus = 0.0;

cout << "PM的年终奖计算方式." << endl;
//...

return bonus;
}
};

class AccountantBonus : public BonusStategy
{
public:
virtual Bonus CalBonus
(const Context& context) const
{
Bonus bonus = 0.0;

cout << "会计的年终奖计算方式." << endl;
//...

return bonus;
}
};

class Staff
{
private:
BonusStategy* m_bonusStategy;
public:
Staff(BonusStategy* bonusStategy)
: m_bonusStategy(bonusStategy){}
Bonus CalBonus() const
{
Bonus bonus = 0.0;
Context context;
//Context的处理...

return m_bonusStategy->CalBonus(context);
}
~Staff()
{
delete m_bonusStategy;
}
};

int main()
{
Staff staff(new ProgrammerBonus);
Bonus bonus = staff.CalBonus();
return 0;
}


方案二恰好解决了方案一不足的两点。其一,每个
Staff
对象中只含有要用到的年终奖计算算法,节省内存以及运行时的CPU。其二,我们可以将
BonusStategy
类们和
Staff
类放在不同的文件下。当我们需要增加职位时,只需要增加一个派生自
BonusStategy
的子类, 对已存在的类不会产生任何影响。当我们需要改变
Staff
的职位时,只需要改变其
m_bonusStategy
成员变量即可。

如,当我们需要增加一个架构师职位,只需要加入下列代码即可:

class ArchitecteBonus : public BonusStategy
{
public:
virtual Bonus CalBonus
(const Context& context) const
{
Bonus bonus = 0.0;

cout << "架构师的年终奖计算方式." << endl;
//...

return bonus;
}
};


综合以上分析,可知:

当选择的算法种类比较少,并且不会增加时,选择方案一可以在满足要求并较为完美地解决问题,并且代码简单易懂,可读性强。

当算法种类较多,或者未来增加算法的可能性较大时,选择方案二可以较好的弥补方案一的不足,可扩展性强。

3.策略模式的一般结构

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: