策略模式(行为型)

前言


在出去旅游的时候,有很多种交通方式均能达到旅游的目的地。可以选择汽车,方便自由,也可以选择火车,速度较快,也可以沿途看风景,路程较远可以选择飞机,时间快。实现某一个功能有多条途径,每一条途径对应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径,这就是本章要将要介绍的策略模式。

策略模式


在策略模式中,定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法(策略),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类,将算法的定义和使用分离,也就是将算法的行为和环境分开

意图

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

参与者

  • Context
    环境类,用一个ConcreteStrategy对象类配置
    维护一个对Strategy对象的引用
    可以定义一个接口来让Strategy访问它的数据

  • Strategy
    抽象策略类,定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy对象定义的算法

  • ConcreteState
    具体策略类,以Strategy接口实现某具体算法
    可以拥有一个Context对象引用访问Context的数据

模式结构

strategy

代码实现

1.首先定义抽象策略类Strategy,声明算法接口AlgorithmInterface()

1
2
3
4
5
6
// Abstract Strategy
class Strategy
{
public:
virtual void AlgorithmInterface() = 0;
};

2.再定义三个具体的策略类ConcreteStrategyAConcreteStrategyBConcreteStrategyC,并实现算法接口AlgorithmInterface()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Concrete Strategy A
class ConcreteStrategyA : public Strategy
{
public:
virtual void AlgorithmInterface()
{
cout << " I am Concrete Strategy A" << endl;
}
};

// Concrete Strategy B
class ConcreteStrategyB : public Strategy
{
public:
virtual void AlgorithmInterface()
{
cout << " I am Concrete Strategy B" << endl;
}
};

// Concrete Strategy C
class ConcreteStrategyC : public Strategy
{
public:
virtual void AlgorithmInterface()
{
cout << " I am Concrete Strategy C" << endl;
}
};

3.再定义策略环境类StrategyContext,具有Strategy对象的引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Context
class StrategyContext
{
private:
Strategy *m_pStrategy;
public:
StrategyContext(Strategy *pS) : m_pStrategy(pS) {}; // 构造静态注入策略对象
virtual ~StrategyContext()
{
SAFE_RELASE_POINTER(m_pStrategy);
}
public:
void SetStrategy(Strategy *pS) // 动态注入策略对象
{
SAFE_RELASE_POINTER(m_pStrategy);// 释放之前申请的对象
m_pStrategy = pS;
}
public:
void ContextInterface()
{
if (NULL_POINTER(m_pStrategy))
{
m_pStrategy->AlgorithmInterface(); // 调用注入的策略对象算法
}
}
};

4.测试策略模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void StrategyTest_General()
{
Strategy *pCSA = new ConcreteStrategyA();
Strategy *pCSB = new ConcreteStrategyB();
Strategy *pCSC = new ConcreteStrategyC();

StrategyContext *pSContext = new StrategyContext(pCSA);
pSContext->ContextInterface();

pSContext->SetStrategy(pCSB);
pSContext->ContextInterface();

pSContext->SetStrategy(pCSC);
pSContext->ContextInterface();

SAFE_RELASE_POINTER(pSContext);
}

5.运行结果:

 I am Concrete Strategy A
 I am Concrete Strategy B
 I am Concrete Strategy C

改进版

改进版1-通过将Strategy作为C++模板参数来实现

在C++中,可利用模板机制用一个Strategy来配置一个类。然而这种技术仅当下面条件满足时才可以使用:

  • 可以在编译时选择Strategy
  • 它不需在运行时改变
    在这种情况下,要被配置的类(如Context)被定义为以一个Strategy类作为一个参数的模板类

使用模板不再需要定义给Strategy定义接口的抽象类。把Strategy作为一个模板参数也使得可以将一个Strategy和它的Context静态地绑定在一起,从而提高效率

1.实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename T>
class StrategyContextEx // StrategyContext以Strategy为模板参数
{
public:
void ContextInterface(void)
{
_strategy.AlgorithmInterface();
}
private:
T _strategy;
};

//把具体的Strategy作为一个模板参数将一个Strategy和它的Context静态地绑定在一起
typedef StrategyContextEx<ConcreteStrategyA> StrategyContextA;
typedef StrategyContextEx<ConcreteStrategyB> StrategyContextB;
typedef StrategyContextEx<ConcreteStrategyC> StrategyContextC;

2.客户端测试代码:

1
2
3
4
5
6
7
8
9
10
11
void StrategyTest_GeneralTemplateEx()
{
StrategyContextA SC_A;
SC_A.ContextInterface();

StrategyContextB SC_B;
SC_B.ContextInterface();

StrategyContextC SC_C;
SC_C.ContextInterface();
}

3.运行结果:

 I am Concrete Strategy A
 I am Concrete Strategy B
 I am Concrete Strategy C

改进版2-通过工厂方法模式来创建具体策略对象

在上述策略模式中,客户端通过new不同的具体策略类来创建对象,对于多个策略可以将创建工作通过工厂方法模式来实现

1.具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Use Factory Pattern to Produce Concrete Strategy

// Abstract Strategy Factory
class StrategyAbstractFactory
{
public:
virtual Strategy* CreateStrategy() = 0;
};

// Concrete Strategy Factory A
class StrategyFactoryA : public StrategyAbstractFactory
{
public:
virtual Strategy* CreateStrategy()
{
return new ConcreteStrategyA();
}
};

// Concrete Strategy Factory B
class StrategyFactoryB : public StrategyAbstractFactory
{
public:
virtual Strategy* CreateStrategy()
{
return new ConcreteStrategyB();
}
};

// Concrete Strategy Factory C
class StrategyFactoryC : public StrategyAbstractFactory
{
public:
virtual Strategy* CreateStrategy()
{
return new ConcreteStrategyC();
}
};

2.客户端调用直接调用工厂方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void StrategyTest_GeneralEx()
{
StrategyAbstractFactory *pSFA = new StrategyFactoryA();
Strategy *pSA = pSFA->CreateStrategy(); // 调用工厂方法创建具体策略对象

StrategyAbstractFactory *pSFB = new StrategyFactoryB();
Strategy *pSB = pSFB->CreateStrategy();

StrategyAbstractFactory *pSFC = new StrategyFactoryC();
Strategy *pSC = pSFC->CreateStrategy();

StrategyContext *pSContext =new StrategyContext(pSA);
pSContext->ContextInterface();

pSContext->SetStrategy(pSB);
pSContext->ContextInterface();

pSContext->SetStrategy(pSC);
pSContext->ContextInterface();

SAFE_RELASE_POINTER(pSFA);
SAFE_RELASE_POINTER(pSFB);
SAFE_RELASE_POINTER(pSFC);
SAFE_RELASE_POINTER(pSContext);
}

3.进一步的,对于工厂方法,同样也可以通过C++模板特性来替换抽象Strategy类的多态特性,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
template <class T>
class StrategyFactoryBase
{
public:
T* CreateStrategy(void)
{
return new T;
}
};

typedef StrategyFactoryBase<ConcreteStrategyA> StrategyFactoryTemplateA;
typedef StrategyFactoryBase<ConcreteStrategyB> StrategyFactoryTemplateB;
typedef StrategyFactoryBase<ConcreteStrategyC> StrategyFactoryTemplateC;

4.客户端使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void StrategyTest_GeneralFactoryTemplateEx()
{
StrategyFactoryTemplateA SFT_A;
Strategy *pSA = SFT_A.CreateStrategy();

StrategyFactoryTemplateB SFT_B;
Strategy *pSB = SFT_B.CreateStrategy();

StrategyFactoryTemplateC SFT_C;
Strategy *pSC = SFT_C.CreateStrategy();

StrategyContext *pSContext = new StrategyContext(pSA);
pSContext->ContextInterface();

pSContext->SetStrategy(pSB);
pSContext->ContextInterface();

pSContext->SetStrategy(pSC);
pSContext->ContextInterface();

SAFE_RELASE_POINTER(pSContext);
}

5.运行结果:

 I am Concrete Strategy A
 I am Concrete Strategy B
 I am Concrete Strategy C

使用场景

  • 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种
  • 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间、时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式
  • 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的与算法相关的数据结构,让算法和对象分开来,使得算法可以独立于使用它的客户而变化
  • 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句

优缺点

  • 优点
    • 相关算法系列 Strategy类层次为Context定义了一系列的可供重用的算法或行为 继承有助于析取出这些算法中的公共功能
    • 将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
    • 消除了一些if else条件语句
    • 实现的选择 Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间、空间权衡取舍要求从不同策略中进行选择
  • 缺点
    • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类,该模式有一个潜在的缺点,就是一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。此时可能不得不向客户暴露具体的实现问题。因此仅当这些不同行为变体与客户相关的行为时才需要使用Strategy模式
    • 无论各个ConcreteStrategy实现的算法是简单还是复杂, 它们都共享Strategy定义的接口。因此很可能某些ConcreteStrategy不会都用到所有通过这个接口传递给它们的信息;简单的ConcreteStrategy可能不使用其中的任何信息。这就意味着有时Context会创建和初始化一些永远不会用到的参数。如果存在这样问题,那么将需要在Strategy和Context之间更进行紧密的耦合
    • 策略模式将造成产生很多策略类

与其他可以消除if-else条件模式的区别(状态模式、策略模式、工厂模式、责任链模式)

1.状态模式

  • 可以认为“状态模式是完全封装且自修改的策略模式”
  • 策略模式只是的条件选择只执行一次;而状态模式是随着实例参数(对象实例的状态)的改变不停地更改执行模式。换言之,策略模式只是在对象初始化的时候更改执行模式;而状态模式是根据对象实例的周期时间而动态地改变对象实例的执行模式
  • 在状态模式中,状态的变迁是由对象的内部条件决定,外界只需关心其接口,不必关心其状态对象的创建和转化;而策略模式里,采取何种策略由外部条件(Context)决定
  • 策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类;而状态模式的环境类由于外在因素需要放进一个具体状态中,以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系
  • 使用策略模式时,客户端需要知道所选的具体策略是哪一个;而使用状态模式时,客户端无须关心具体状态,环境类的状态会根据用户的操作自动转换
  • 如果系统中某个类的对象存在多种状态,不同状态下行为有差异,而且这些状态之间可以发生转换时使用状态模式;如果系统中某个类的某一行为存在多种实现方式,而且这些实现方式可以互换时使用策略模式

2.工厂模式

  • 工厂模式是创建型模式,它关注对象创建提供创建对象的接口让对象的创建与具体的使用客户无关;策略模式是对象行为型模式,它关注行为和算法的封装。它定义一系列的算法,把每一个算法封装起来,,并且使它们可相互替换,使得算法可独立于使用它的客户而变化

3.责任链模式

  • 状态与责任链两个设计模式最大的区别就是状态模式是让各个状态对象自己知道其下一个处理的对象是谁;而职责链模式中的各个对象并不指定其下一个处理的对象到底是谁,只有在客户端才设定

策略模式具体实例


旅游交通工具选择问题

使用策略模式简单实现前言所述的旅游交通工具选择问题

代码实现
1.定义抽象旅游策略类TravelStrategy,并声明Travel()接口:

1
2
3
4
5
6
// Abstract Travel Strategy
class TravelStrategy
{
public:
virtual void Travel() = 0;
};

2.定义三种具体的旅游类CarStrategyAirplaneStrategyTrainStrategy,并实现Travel()接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Concrete Travel Strategy : Car Strategy
// Singleton & Atuo memory delete
class CarStrategy : public TravelStrategy
{
private:
static CarStrategy *m_pStrategy;
class Garbage
{
~Garbage()
{
if (!NULL_POINTER(CarStrategy::GetInstance()))
{
delete CarStrategy::GetInstance();
}
}
};
static Garbage g;
public:
static CarStrategy* GetInstance()
{
if (NULL_POINTER(m_pStrategy))
{
m_pStrategy = new CarStrategy();
}

return m_pStrategy;
}
private:
CarStrategy() {};
CarStrategy(const CarStrategy&) {};
CarStrategy& operator = (const CarStrategy&) {};
public:
virtual void Travel()
{
cout << "Select The Way of Car For Traveling" << endl;
}
};
CarStrategy* CarStrategy::m_pStrategy = NULL;

// Concrete Travel Strategy : Airplane Strategy
// Singleton & Atuo memory delete
class AirplaneStrategy : public TravelStrategy
{
private:
static AirplaneStrategy *m_pStrategy;
class Garbage
{
~Garbage()
{
if (!NULL_POINTER(AirplaneStrategy::GetInstance()))
{
delete AirplaneStrategy::GetInstance();
}
}
};
static Garbage g;
public:
static AirplaneStrategy* GetInstance()
{
if (NULL_POINTER(m_pStrategy))
{
m_pStrategy = new AirplaneStrategy();
}

return m_pStrategy;
}
private:
AirplaneStrategy() {};
AirplaneStrategy(const AirplaneStrategy&) {};
AirplaneStrategy& operator = (const AirplaneStrategy&) {};
public:
virtual void Travel()
{
cout << "Select The Way of Airplane For Traveling" << endl;
}
};
AirplaneStrategy* AirplaneStrategy::m_pStrategy = NULL;

// Concrete Travel Strategy : Train Strategy
// Singleton & Atuo memory delete
class TrainStrategy : public TravelStrategy
{
private:
static TrainStrategy *m_pStrategy;
class Garbage
{
~Garbage()
{
if (!NULL_POINTER(TrainStrategy::GetInstance()))
{
delete TrainStrategy::GetInstance();
}
}
};
static Garbage g;
public:
static TrainStrategy* GetInstance()
{
if (NULL_POINTER(m_pStrategy))
{
m_pStrategy = new TrainStrategy();
}

return m_pStrategy;
}
private:
TrainStrategy() {};
TrainStrategy(const TrainStrategy&) {};
TrainStrategy& operator = (const TrainStrategy&) {};
public:
virtual void Travel()
{
cout << "Select The Way of Train For Traveling" << endl;
}
};
TrainStrategy* TrainStrategy::m_pStrategy = NULL;

3.定义旅游的上下文调用类Person, 具有TravelStrategy的一个对象引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Travel Context : Person
class Person
{
private:
TravelStrategy *m_pTravelStrategy;
public:
Person(TravelStrategy *pTS) : m_pTravelStrategy(pTS) {};
public:
void SetTravelStrategy(TravelStrategy *pTS) // 有Garbage类,不需要手动释放ConcreteTravelStrategy对象了
{
m_pTravelStrategy = pTS;
}
public:
void Travel()
{
m_pTravelStrategy->Travel(); // 委托调用具体Strategy的Travel方法
}
};

4.测试策略模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
void StrategyTest_Travel()
{
Person *pPerson = new Person(CarStrategy::GetInstance());
pPerson->Travel();

pPerson->SetTravelStrategy(AirplaneStrategy::GetInstance());
pPerson->Travel();

pPerson->SetTravelStrategy(TrainStrategy::GetInstance());
pPerson->Travel();

SAFE_RELASE_POINTER(pPerson);
}

5.运行结果:

Select The Way of Car For Traveling
Select The Way of Airplane For Traveling
Select The Way of Train For Traveling