策略模式:指对象(被称为上下文)有某个行为,但是在不同的场景中,该行为有不同的实现算法。我们可以把算法单独提出来形成一个继承体系,然后让上下文包含某个算法即可。这样,当我们需要添加算法时,只需要继承某个抽象基类然后实现自己的算法即可,不需要修改其它已经实现的算法;当需要修改某个上下文所包含的算法时,也只需要修改
上下文
内指向算法的指针即可。策略模式的UML图如下:
下面这个例子是使用C++编写的一个带策略模式的计算器:
#include <iostream>
using namespace std;
// 抽象策略基类
class Strategy {
public:
virtual void Algorithm(const double lhs, const double rhs) = 0;
};
class StrategyAdd : public Strategy {
public:
void Algorithm(const double lhs, const double rhs)
{
cout << lhs + rhs << endl;
}
};
class StrategySub : public Strategy {
public:
void Algorithm(const double lhs, const double rhs)
{
cout << lhs - rhs << endl;
}
};
class StrategyMul : public Strategy {
public:
void Algorithm(const double lhs, const double rhs)
{
cout << lhs * rhs << endl;
}
};
class StrategyDiv : public Strategy {
public:
void Algorithm(const double lhs, const double rhs)
{
if (rhs == 0)
throw;
cout << lhs / rhs << endl;
}
};
// 给用户使用的类,包含某个策略
class Calculator {
public:
Calculator(const char type)
{
// 利用工厂模式实例化策略
switch (type)
{
case '+':
strategy = new StrategyAdd();
break;
case '-':
strategy = new StrategySub();
break;
case '*':
strategy = new StrategyMul();
break;
case '/':
strategy = new StrategyDiv();
break;
default:
throw runtime_error("Invalid strategy!");
break;
}
}
~Calculator()
{
delete strategy; // 别忘记删除指针
}
// 该函数调用具体策略中的算法
void Calculate(const double lhs, const double rhs)
{
strategy->Algorithm(lhs, rhs);
}
private:
Strategy *strategy; // 保存策略的指针
};
int main()
{
Calculator add('+');
Calculator sub('-');
Calculator mul('*');
Calculator div('/');
add.Calculate(10, 20);
sub.Calculate(30, 40);
mul.Calculate(11, 11);
div.Calculate(100, 8);
system("pause");
return 0;
}
运行结果:
在这个例子中,上下文就是Calculator计算器类,它包含的算法可能需要改变,具体需要使用哪个算法可根据用户代码和工厂模式确定,算法的调用则利用到了多态性质。Strategy是一个抽象算法类,它包含一个纯虚函数。凡是继承该抽象算法类并实现了该纯虚函数的类就能够成为一个具体算法类,例如这里的四则运算算法类。策略模式使得用户代码所需的上下文和上下文所关联的算法分离,降低了耦合度。
这个例子将策略模式和工厂模式结合,使得算法的实例化过程对用户代码完全透明。但还有一个问题,当增加了一个新的算法之后,需要在上下文Calculator中的switch分支语句中添加相应的case分支,这增加了修改成本。解决这个问题的方法是使用反射技术,学到再写。
参考:
《大话设计模式》第2章。