在GOF的《设计模式:可复用面向对象软件的基础》一书中对策略模式是这样说的:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。该模式使得算法可独立于使用它的客户而变化。
策略模式为了适应不同的需求,只把变化点封装了,这个变化点就是实现不同需求的算法,但是,用户需要知道各种算法的具体情况。就像上面的加班工资,不同的加班情况,有不同的算法。我们不能在程序中将计算工资的算法进行硬编码,而是能自由的变化的。这就是策略模式。
方式一:直接通过参数指定,传入一个特定算法的指针。
class Strategy
{
public:
virtual void Replace() = 0;
};
class StrategyA : public Strategy
{
public:
void Replace() {std::cout<<"StrategyA"<<std::endl;}
};
class StrategyB : public Strategy
{
public:
void Replace() {std::cout<<"StrategyB"<<std::endl;}
};
class StrategyC : public Strategy
{
public:
void Replace() {std::cout<<"StrategyC"<<std::endl;}
};
class Context
{
private:
Strategy * m_pStrategy;
public:
Context(Strategy * pStrategy)
{
m_pStrategy = pStrategy;
}
~Context()
{
delete m_pStrategy;
}
void Replace()
{
m_pStrategy->Replace();
}
};
int main()
{
Context contextA(new StrategyA());
contextA.Replace();
Context contextB(new StrategyB());
contextB.Replace();
Context contextC(new StrategyC());
contextC.Replace();
return 0;
}
如果用这种方式,客户就需要知道这些算法的具体定义,可以看到暴露了太多的细节。在main函数中,也就是在客户端使用策略模式时,会创建非常多的Strategy,而这样就莫名的增加了客户端的压力,让客户端的复杂度陡然增加了。
方式二:使策略模式和简单工厂模式相结合,从而减轻客户端的压力。也是直接通过参数指定,只不过不是传入指针,而是一个标签。这样客户只要知道算法的相应标签即可,而不需要知道算法的具体定义。
class Context
{
private:
Strategy * m_pStrategy;
public:
Context(int type)
{
if(type == 1)
{
m_pStrategy = new StrategyA();
}
else if(type == 2)
{
m_pStrategy = new StrategyB();
}
else if(type == 3)
{
m_pStrategy = new StrategyC();
}
else
{
m_pStrategy = NULL;
}
}
~Context()
{
delete m_pStrategy;
}
void Replace()
{
m_pStrategy->Replace();
}
};
int main()
{
Context contextA(1);
contextA.Replace();
Context contextB(2);
contextB.Replace();
Context contextC(3);
contextB.Replace();
return 0;
}
上面两种方式,构造函数都需要形参。构造函数是否可以不用参数呢?下面给出第三种实现方式。
方式三:利用模板实现。算法通过模板的实参指定。当然了,还是使用了参数,只不过不是构造函数的参数。在策略模式中,参数的传递难以避免,客户必须指定某种算法。
template<class T>
class Context
{
private:
T m_pStrategy;
public:
void Replace()
{
m_pStrategy.Replace();
}
};
int main()
{
Context<StrategyA> contextA;
contextA.Replace();
return 0;
}
优点
算法可以自由切换
避免使用多重条件判断(如果不用策略模式我们可能会使用多重条件语句,不利于维护)
扩展性良好,增加一个策略只需实现接口即可
缺点
策略类数量会增多,每个策略都是一个类,复用的可能性很小
所有的策略类都需要对外暴露
使用场景
多个类只有算法或行为上稍有不同的场景
算法需要自由切换的场景
需要屏蔽算法规则的场景
应用实例
出行方式,自行车、汽车等,每一种出行方式都是一个策略
商场促销方式,打折、满减等
策略模式和工厂模式的区别: 工厂模式中只管生产实例,具体怎么使用工厂实例由调用方决定,策略模式是将生成实例的使用策略放在策略类中配置后才提供调用方使用。 工厂模式调用方可以直接调用工厂实例的方法属性等,策略模式不能直接调用实例的方法属性,需要在策略类中封装策略后调用