1、什么是策略模式?
策略模式,又叫算法簇模式,就是定义了不同的算法簇,并且之间可以互相替换,此模式算法的变化独立于使用算法的客户。
2、策略模式有什么好处?
策略模式的好处在于你可以动态改变对象的行为。
3、设计原则
设计原则是把一个类中经常改变或者将来改变的部分提取出来,作为一个接口(C++中可以用抽象类),然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现这个接口的类的行为。
策略模式属于对象行为型模式,主要针对一组算法,将每一个算法封装到具有共同接口(C++中即为抽象基类)的独立的类中,从而使得他们可以相互替换。策略模式使得算法可以在不影响客户端的情况下发生变化。
4、策略模式中有三个对象:
(1)环境对象:该类中实现了对抽象策略中定义的接口或者抽象类的引用。
(2)抽象策略对象:它可由接口或者抽象类来实现。
(3)具体策略对象:它封装了实现不同功能的不同算法。
5、适用性
当存在以下情况时,使用策略模式
(1)“策略”提供一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
(2)需要使用一个算法的不同变体。例如,你可能会定义一些反应不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
(3)算法使用了客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
(4)一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入他们各自的Strategy类中以代替这些条件语句。
6、结构
7、应用场景举例:
刘备要到江东娶老婆了,走之前诸葛亮给赵云(伴郎)三个锦囊妙计,说是按天机拆开能解决棘手问题,嘿,还别说,真解决了大问题,搞到最后是周瑜陪了夫人又折兵,那咱们先看看这个场景是什么样子的。
先说说这个场景中的要素:三个妙计,一个锦囊,一个赵云,妙计是亮哥给的,妙计放在锦囊里,俗称就是锦囊妙计嘛,那赵云就是一个干活的人,从锦囊取出妙计,执行,然后获胜。
先来看看图。
解释:
main(),赵云
CContext,锦囊
CStrategy,策略抽象类
CBackDoor,策略之一
CGivenGreenLight,策略之二
CBlockEnemy,策略之三
说明:一个策略放到一个锦囊里。当用的时候,找到这个锦囊,从锦囊里拿出策略来使用。
注意:锦囊只是简单的装载和调用策略,锦囊里没有逻辑。策略会有更大的自主权,运行更多的逻辑。
代码:
三个策略继承自抽象类CStrategy,首先是CStrategy类的定义:
1 #ifndef _STRATEGY_H_ 2 #define _STRATEGY_H_ 3 class CStrategy{ 4 public: 5 CStrategy(void){}; 6 virtual ~CStrategy(void){}; 7 virtual void Operate(void) = 0;//纯虚函数,这样每个继承自CStrategy的类都可以实现一个自身的Operate()函数,即实现各自的策略 8 }; 9 #endif
然后再写三个实现类,因为有三个策略:
妙计一:初到吴国:
BackDoor.h
1 #ifndef _BACKDOOR_H_ 2 #define _BACKDOOR_H_ 3 #include "Strategy.h" 4 class CBackDoor :public CStrategy{ 5 public: 6 CBackDoor(void); 7 ~CBackDoor(void); 8 void Operate(void); 9 }; 10 #endif
BackDoor.cpp
1 #include "BackDoor.h" 2 #include <iostream> 3 using namespace std; 4 //构造函数 5 CBackDoor::CBackDoor(void) 6 { 7 8 } 9 10 //析构函数 11 CBackDoor::~CBackDoor(void) 12 { 13 14 } 15 16 void CBackDoor::Operate(void) 17 { 18 cout << "找乔国老帮忙,让吴国太给孙权施压" << endl; 19 }
妙计二:求吴国太开绿灯,放行:
GivenGreenLight.h
1 #ifndef _GIVENGREENLIGHT_H_ 2 #define _GIVENGREENLIGHT_H_ 3 #include "Strategy.h" 4 5 class CGivenGreenLight:public CStrategy{ 6 public: 7 CGivenGreenLight(void); 8 ~CGivenGreenLight(void); 9 void Operate(void); 10 }; 11 #endif
GivenGreenLight.cpp
1 #include "GivenGreenLight.h" 2 #include <iostream> 3 using namespace std; 4 5 CGivenGreenLight::CGivenGreenLight(void) 6 { 7 8 } 9 10 CGivenGreenLight::~CGivenGreenLight(void) 11 { 12 13 } 14 15 void CGivenGreenLight::Operate(void) 16 { 17 cout << "求吴国太开个绿灯,放行!" << endl; 18 }
妙计三:孙夫人断后,挡住追兵:
BlockEnemy.h
1 #ifndef _BLOCKENEMY_H_ 2 #define _BLOCKENEMY_H_ 3 #include "Strategy.h" 4 class CBlockEnemy :public CStrategy{ 5 public: 6 CBlockEnemy(void); 7 ~CBlockEnemy(void); 8 void Operate(void); 9 10 }; 11 #endif
BlockEnemy.cpp
1 #include "BlockEnemy.h" 2 #include <iostream> 3 using namespace std; 4 5 CBlockEnemy::CBlockEnemy(void) 6 { 7 8 } 9 10 CBlockEnemy::~CBlockEnemy(void) 11 { 12 13 } 14 15 void CBlockEnemy::Operate(void) 16 { 17 cout << "孙夫人断后,挡住追兵" << endl; 18 }
好了,三个妙计是有了,还需要一个地方放妙计,即锦囊:
Context.h
1 #ifndef _CONTEXT_H_ 2 #define _CONTEXT_H_ 3 #include "Strategy.h" 4 //定义环境(锦囊)存策略 5 class CContext{ 6 public: 7 CContext(CStrategy* pStrategy); 8 ~CContext(void); 9 void Operate(void); 10 private: 11 CStrategy* m_pStrategy; 12 }; 13 #endif
Context.cpp
1 #include "Context.h" 2 //构造函数中传入具体的策略,使用抽象基类CStrategy指针类型 3 //指向策略 4 //CStrategy中的Operate()函数定义为纯虚函数,如此可实现多态:在不同的类中使用不同的operate()函数 5 CContext::CContext(CStrategy* pStrategy) 6 { 7 this->m_pStrategy = pStrategy; 8 } 9 //析构函数 10 CContext::~CContext(void) 11 { 12 delete this->m_pStrategy; //销毁CStrategy类指针 13 } 14 15 //操作具体的策略 16 void CContext::Operate(void) 17 { 18 this->m_pStrategy->Operate(); 19 }
然后就是赵云护送刘备去娶亲了。看看执行过程吧。
excute.cpp
1 #include <iostream> 2 #include <tchar.h> 3 #include "Context.h" 4 #include "BackDoor.h" 5 #include "GivenGreenLight.h" 6 #include "BlockEnemy.h" 7 using namespace std; 8 9 int _tmain(int argc, _TCHAR** argv) 10 { 11 //定义一个CContext类的指针 12 CContext* pContext; 13 14 cout << "\n\n\n\n" << endl; 15 cout << "----------刚刚到吴国的时候拆第一个----------" << endl; 16 //使用关键字new在堆上动态创建一个对象 17 /* 18 它实际上做了三件事: 19 获得一块内存空间 20 调用构造函数 21 返回正确的指针。 22 */ 23 pContext = new CContext(new CBackDoor); 24 pContext->Operate(); 25 /* 26 delete时做了两件事: 27 1、调用 pContext指向对象的析构函数,对打开的文件进行关闭。 28 2、释放该对象的内存 29 */ 30 delete pContext; 31 32 cout << "\n\n\n\n" << endl; 33 cout << "----------刘备乐不思蜀了,拆第二个了----------" << endl; 34 pContext = new CContext(new CGivenGreenLight); 35 pContext->Operate(); 36 delete pContext; 37 38 cout << "\n\n\n\n" << endl; 39 cout << "----------孙权的小兵追了,咋办?拆第三个----------" << endl; 40 pContext = new CContext(new CBlockEnemy); 41 pContext->Operate(); 42 delete pContext; 43 44 /* 45 _CrtSetDbgFlag: 46 检索或修改 _crtDbgFlag 标志的状态,以控制调试堆管理器的分配行为 47 _CRTDBG_ALLOC_MEM_DF: 48 启用调试堆分配并使用内存块类型标识符,例如 _CLIENT_BLOCK。 49 _CRTDBG_LEAK_CHECK_DF: 50 在程序退出时通过对 _CrtDumpMemoryLeaks 的调用执行自动泄露检查,如果应用程序无法释放它分配的所有内存,则生成错误报告。 51 */ 52 _CrtSetDbgFlag(-_CRTDBG_ALLOC_MEM_DF || _CRTDBG_LEAK_CHECK_DF); 53 _CrtDumpMemoryLeaks(); 54 getchar(); 55 return 0; 56 57 }
运行结果:
8、总结与分析
(1)策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得他们可以互换。”
(2)在策略模式中,应该由客户端自己决定在什么情况下使用什么具体策略。
(3)策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端决定。这在一定成都上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。