[设计模式]Strategy策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改这种类型的设计模式属于行为型模式

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法

策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换。对照类图可以看到,策略模式与模版方法模式的区别仅仅是多了一个单独的封装类Context,它与模版方法模式的区别在于:在模版方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Context中抽象策略Strategy一般是一个接口,目的只是为了定义规范,里面一般不包含逻辑其实,这只是通用实现,而在实际编程中,因为各个具体策略实现类之间难免存在一些相同的逻辑,为了避免重复的代码,我们常常使用抽象类来担任Strategy的角色,在里面封装公共的代码,因此,在很多应用的场景中,在策略模式中一般会看到模版方法模式的影子。Strategy模式和Template模式要解决的问题是类似的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的解耦。

Strategy策略模式

定义一系列的算法,把每一个算法封装起来,并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而独立变化。

解析:简而言之一句话,Strategy模式是对算法的封装处理一个问题的时候可能有多种算法,这些算法的接口(输入参数,输出参数等)都一致的,那么可以考虑采用Strategy模式对这些算法进行封装,在基类中定义一个函数接口就可以了。

分析下定义,策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口。

组成

  • 抽象策略: 策略类,通常由一个接口或者抽象类实现。当各个实现类中存在着重复的逻辑时,则使用抽象类来封装这部分公共的代码,此时,策略模式看上去更像是模版方法模式。
  • 具体策略:通常由一组封装了算法的类来担任,这些类之间可以根据需要自由替换。
  • 环境类:也叫上下文,对策略进行二次封装,目的是避免高层模块对策略的直接调用,持有一个策略类的引用,最终给客户端调用。
  1. 主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
  2. 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
  3. 应用实例:旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。
  4. 优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。4、策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  5. 缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。3.客户端必须知道所有的策略类,并自行决定使用哪一个策略类。4.策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。

模式分析

  • 在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。
  • 策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。

应用场景

  1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  2. 一个系统需要动态地在几种算法中选择一种。
  3. 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

小demo

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模式则没有

策略模式和工厂模式的对比

两个模式比较重要的一个区别在于:工厂模式是创建型的设计模式,偏重于创建不同的对象,而策略模式是行为型的设计模式,偏重于通过不同的方式实现同样的行为

反过来,创建对象本用的就是一个方法,通过不同的工厂动态指定实际的创建方法,就是封装了这个方法,工厂模式的目的最终是为了创建对象,而策略模式的目的并不仅限于创建对象,可以说工厂模式应用了策略模式,更好地说法是,抽象工厂模式和策略模式都应用了面向接口编程的思想。
策略模式是一种简单常用的模式,我们在进行开发的时候,会经常有意无意地使用它,一般来说,策略模式不会单独使用,跟模版方法模式、工厂模式等混合使用的情况比较多。

你可能感兴趣的:(模式架构)