在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换。对照类图可以看到,策略模式与模版方法模式的区别仅仅是多了一个单独的封装类Context,它与模版方法模式的区别在于:在模版方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Context中,抽象策略Strategy一般是一个接口,目的只是为了定义规范,里面一般不包含逻辑。其实,这只是通用实现,而在实际编程中,因为各个具体策略实现类之间难免存在一些相同的逻辑,为了避免重复的代码,我们常常使用抽象类来担任Strategy的角色,在里面封装公共的代码,因此,在很多应用的场景中,在策略模式中一般会看到模版方法模式的影子。Strategy模式和Template模式要解决的问题是类似的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的解耦。
定义一系列的算法,把每一个算法封装起来,并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而独立变化。
解析:简而言之一句话,Strategy模式是对算法的封装。处理一个问题的时候可能有多种算法,这些算法的接口(输入参数,输出参数等)都一致的,那么可以考虑采用Strategy模式对这些算法进行封装,在基类中定义一个函数接口就可以了。
分析下定义,策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口。
strategy.h
#ifndef STRATEGY_H
#define STRATEGY_H
class Strategy;
//这个类是Strategy模式的关键,也是Strategy模式和Template模式的根本区别所在。
//Strategy通过"组合"(委托)方式实现算法的异构,而Template模式则采取的是继承的方式。
//这两个模式的区别也是继承和组合两种实现接口重用的方式的区别
class Context
{
public:
Context(Strategy *pStrategy) : m_pStrategy(pStrategy){}
~Context();
void ContextInterface();
private:
Strategy* m_pStrategy;
};
class Strategy
{
public:
virtual ~Strategy(){}
virtual void AlgorithmInterface() = 0;
};
class ConcreateStrategyA : public Strategy
{
public:
virtual ~ConcreateStrategyA(){}
virtual void AlgorithmInterface();
};
class ConcreateStrategyB : public Strategy
{
public:
virtual ~ConcreateStrategyB(){}
virtual void AlgorithmInterface();
};
#endif
strategy.cpp
#include
#include "Strategy.h"
Context::~Context()
{
delete m_pStrategy;
m_pStrategy = NULL;
}
void Context::ContextInterface()
{
if (NULL != m_pStrategy)
{
m_pStrategy->AlgorithmInterface();
}
}
void ConcreateStrategyA::AlgorithmInterface()
{
std::cout << "AlgorithmInterface Implemented by ConcreateStrategyA\n";
}
void ConcreateStrategyB::AlgorithmInterface()
{
std::cout << "AlgorithmInterface Implemented by ConcreateStrategyB\n";
}
main.cpp
#include "Strategy.h"
#include
int main()
{
Strategy* pStrategy = new ConcreateStrategyA();
Context* pContext = new Context(pStrategy);
pContext->ContextInterface();
delete pContext;
system("pause");
return 0;
}
//Stategy模式的代码很直观,关键是将算法的逻辑封装到一个类中
Strategy模式和Template模式实际上是实现一个抽象接口的两种方式:继承和组合之间的区别。
要实现一个抽象接口,
通过上面的示例可以看出,策略模式仅仅封装算法,提供新的算法插入到已有系统中,以及可以把不需要的算法从系统中除去,策略模式本身不决定在何时使用何种算法,在什么情况下使用什么算法是由客户端决定的。
策略模式的一个很重要的特点:运行时策略的唯一性——也就是策略模式运行期间,客户端在每一个时刻只能使用一个具体的策略实现,虽然可以动态地在不同的策略实现中切换,但同时只能使用一个。
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指Context在运行过程中由于一些条件发生改变而使得State 对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
两者之间的区别就是State模式中具体实现类中有一个指向Context的引用,而Strategy模式则没有。
两个模式比较重要的一个区别在于:工厂模式是创建型的设计模式,偏重于创建不同的对象,而策略模式是行为型的设计模式,偏重于通过不同的方式实现同样的行为。
反过来,创建对象本用的就是一个方法,通过不同的工厂动态指定实际的创建方法,就是封装了这个方法,工厂模式的目的最终是为了创建对象,而策略模式的目的并不仅限于创建对象,可以说工厂模式应用了策略模式,更好地说法是,抽象工厂模式和策略模式都应用了面向接口编程的思想。
策略模式是一种简单常用的模式,我们在进行开发的时候,会经常有意无意地使用它,一般来说,策略模式不会单独使用,跟模版方法模式、工厂模式等混合使用的情况比较多。