在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。
在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。
如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。
策略模式的定义与特点:
策略模式定义如下:
策略模式(Strategy Pattern):定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式。 |
在策略模式中,我们可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类我们都可以称之为一种策略(Strategy),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类。
策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合“依赖倒转原则”。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可
策略模式的适用场景
在以下情况下可以考虑使用策略模式:
- 一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,根据“里氏代换原则”和面向对象的多态性,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。
- 一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。
- 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性。
策略模式的主要优点如下:
- 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
- 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
- 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
- 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
- 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
其主要缺点如下:
- 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
- 策略模式造成很多的策略类。
策略模式的结构与实现:
策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性,现在我们来分析其基本结构和实现方法。
1. 模式的结构
策略模式的主要角色如下。
- 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
其结构图如图 1 所示。
模式实现:
// 策略模式简单框架
#include <iostream>
//#include <vld.h>
using namespace std;
/* 抽象算法类,Strategy类,定义所有支持算法的公共接口 */
class Strategy
{
public:
/* 算法方法接口 */
virtual void AlgorithmInterface() = 0;
};
/* ConcreteStrategy类,封装了具体的算法和行为,继承于Strategy */
/* 具体算法A */
class ConcreteStrategyA : public Strategy
{
public:
/* 算法A实现方法 */
void AlgorithmInterface()
{
cout << "Algorithm A achieved" << endl;
}
};
/* 具体算法B */
class ConcreteStrategyB : public Strategy
{
public:
/* 算法B实现方法 */
void AlgorithmInterface()
{
cout << "Algorithm B achieved" << endl;
}
};
/* 具体算法C */
class ConcreteStrategyC : public Strategy
{
public:
/* 算法C实现方法 */
void AlgorithmInterface()
{
cout << "Algorithm C achieved" << endl;
}
};
/* Context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用 */
/* 上下文 */
class Context
{
public:
/* 初始化时,传入具体的策略对象 */
Context(Strategy* strategy):mstrategy(strategy){}
/* 上下文接口:根据具体的策略对象,调用其算法的方法 */
void ContextInterface()
{
mstrategy->AlgorithmInterface();
}
private:
Strategy* mstrategy;
};
int main()
{
Strategy* strategyA = new ConcreteStrategyA();
Context context1(strategyA);
context1.ContextInterface();
Strategy* strategyB = new ConcreteStrategyB();
Context context2(strategyB);
context2.ContextInterface();
Strategy* strategyC = new ConcreteStrategyC();
Context context3(strategyC);
context3.ContextInterface();
delete strategyC;
delete strategyB;
delete strategyA;
}
策略模式的具体应用:
1.模拟影院售票系统:
https://zhaoyang.blog.youkuaiyun.com/article/details/89521447
2.策略模式在“大闸蟹”做菜中的应用:
http://c.biancheng.net/view/1378.html