策略模式
1、背景
在我们的生活中我们可以通过很多种不同的方式来完成一件事情,这里的每一种方式都可以称作为一种策略。我们可以根据环境、条件等因素的不同选择不同的策略来完成这件事情。比如:
某软件公司为某电影院开发了一套电影院售票系统,在该系统中需要为不同类型的用户提供不同的电影票的打折方式,具体打折方案如下:
- (1)学生凭学生证可享受票价8折优惠
- (2)年龄在10周岁及以下的儿童可享受每张票减免10元的优惠(原始票价需大于等于20元)
- (3)影院VIP用户除享受票价半折优惠外还可进行积分,积分累计到一定额度可换取电影院赠送的礼品
- (4)该系统在将来可能还要引入新的打折方式
该如何设计这个系统?
所以:策略模式为了适应不同的需求,只把变化点封装了,这个变化点就是实现不同需求的算法,但是,用户需要知道各种算法的具体情况
2、定义
**策略模式:**定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。该模式使得算法可独立于使用它的客户而变化。
- 简言之,策略模式封装了变化
- 1) Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个功能)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
和Strategy是聚合关系,has-a
- 2) Strategy(抽象策略类):抽象策略类为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或者具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
和ConcreteStrategy是继承关系,is-a
- 3) ConcreteStrategy(具体策略类):具体策略类实现了在抽象策略类中声明的算法,在运行时具体策略类将覆盖在环境类所定义的抽象策略类对象,使用一种具体的算法实现某个业务功能。
3、特征
- 1、实现在同一个Strategy接口,策略之间便于转换
- 2、算法封装在具体的ConcreteStrategy中,所以可以在不影响Context正常条件使用的情况下,改进相关的算法。因为每个算法都有自己的类,可以通过自己的接口单独测试
- 3、策略模式是用来封装算法的,在实践中可以用来封装所有的算法,一旦问题需求涉及不同时间应用不同的业务规则,可以考虑策略模式
- 4、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量
- 5、 在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象
4、应用
1、基本的策略模式
#include <iostream>
using namespace std;
class Strategy { // 抽象算法类
public:
virtual void AlgorithmInterface() = 0; //纯虚函数,派生类需要实例化
virtual ~Strategy() {}
};
class ConcreteStrategyA : public Strategy { // 具体算法A
public:
void AlgorithmInterface() {
cout << "ConcreteStrategyA" << endl;
}
};
class ConcreteStrategyB : public Strategy { // 具体算法B
public:
void AlgorithmInterface() {
cout << "ConcreteStrategyB" << endl;
}
};
class Context { // 上下文
private:
Strategy* strategy;//聚合关系,定义一个strategy局部对象
public:
Context(Strategy* s):strategy (s)//构造函数
{}
void ContextInterface() { // 上下文接口
strategy->AlgorithmInterface();//通过公关接口调用不同算法
}
};
int main() {
Strategy* s = new ConcreteStrategyA();//基类指针指向派生类对象
Context* c = new Context(s);
c->ContextInterface();
delete s;
delete c;
return 0;
}
2、策略与简单工厂的结合
#include <iostream>
using namespace std;
class Strategy { // 抽象算法类
public:
virtual void AlgorithmInterface() = 0;
virtual ~Strategy() {}
};
class ConcreteStrategyA : public Strategy { // 具体算法A
public:
void AlgorithmInterface() {
cout << "ConcreteStrategyA" << endl;
}
};
class ConcreteStrategyB : public Strategy { // 具体算法B
public:
void AlgorithmInterface() {
cout << "ConcreteStrategyB" << endl;
}
};
class ContextFactory { // 上下文
private:
Strategy* strategy;
public:
ContextFactory(char c) {
switch (c) {
case 'A': strategy = new ConcreteStrategyA(); break;
case 'B': strategy = new ConcreteStrategyB(); break;
default: strategy = NULL;
}
}
void ContextInterface() { // 上下文接口
strategy->AlgorithmInterface();
}
~ContextFactory() { delete strategy; }
};
int main() {
ContextFactory* cf = new ContextFactory('A');
cf->ContextInterface();
delete cf;
return 0;
}
基本的策略模式代码中,用户选择部分(main函数):
Strategy* s = new ConcreteStrategyA();
Context* c = new Context(s);
c->ContextInterface();
策略与简单工厂的结合,用户选择部分(main函数):
ContextFactory* cf = new ContextFactory('A');
cf->ContextInterface();
可以发现:
- 基本的策略模式,让客户端认识两个类,
Strategy和Context
- 策略与简单工厂的结合模式,让客户端认识一个类
ContextFactory
,
耦合度(客户和服务的关联认识度)降低。我们在客户端实例化ContextFactory
对象,调用strategy
的AlgorithmInterface()
公共接口(纯虚函数),这使得算法接口彻底与客户端分离。连算法的父类strategy
也接触不到