意图
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
动机
策略模式为了适应不同的需求,只把变化点封装了,这个变化点就是实现不同需求的算法,但是,用户需要知道各种算法的具体情况。就像上面的加班工资,不同的加班情况,有不同的算法。我们不能在程序中将计算工资的算法进行硬编码,而是能自由的变化的。这就是策略模式。
刚刚加班回来;哎,公司规定平时加班只有10块钱的餐补;星期六和星期天加班,只给串休假;在国家规定的节假日按照3倍工资发放。那么对于这么多的计算加班费的方法,公司的OA系统是如何进行做的呢?各种计算加班费的情况就可以认为是策略模式
适用性
- 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式
- 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
- 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
结构
参与者
Strategy:定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法;
ConcreteStrategy:实现Strategy接口的具体算法;
Context:使用一个ConcreteStrategy对象来配置;维护一个对Stategy对象的引用,同时,可以定义一个接口来让Stategy访问它的数据。
效果
相关算法系列Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
实现
纯策略模式,实际上就是父类为纯虚函数,子类根据需求实现各种方法
1 #include <iostream> 2 using namespace std; 3 4 // The abstract strategy 5 class Strategy 6 { 7 public: 8 virtual void AlgorithmInterface() = 0; 9 }; 10 11 class ConcreteStrategyA : public Strategy 12 { 13 public: 14 void AlgorithmInterface() 15 { 16 cout<<"I am from ConcreteStrategyA."<<endl; 17 } 18 }; 19 20 class ConcreteStrategyB : public Strategy 21 { 22 public: 23 void AlgorithmInterface() 24 { 25 cout<<"I am from ConcreteStrategyB."<<endl; 26 } 27 }; 28 29 class ConcreteStrategyC : public Strategy 30 { 31 public: 32 void AlgorithmInterface() 33 { 34 cout<<"I am from ConcreteStrategyC."<<endl; 35 } 36 }; 37 38 class Context 39 { 40 public: 41 Context(Strategy *pStrategyArg) : pStrategy(pStrategyArg) 42 { 43 } 44 void ContextInterface() 45 { 46 pStrategy->AlgorithmInterface(); 47 } 48 private: 49 Strategy *pStrategy; 50 }; 51 52 int main() 53 { 54 // Create the Strategy 55 Strategy *pStrategyA = new ConcreteStrategyA; 56 Strategy *pStrategyB = new ConcreteStrategyB; 57 Strategy *pStrategyC = new ConcreteStrategyC; 58 Context *pContextA = new Context(pStrategyA); 59 Context *pContextB = new Context(pStrategyB); 60 Context *pContextC = new Context(pStrategyC); 61 pContextA->ContextInterface(); 62 pContextB->ContextInterface(); 63 pContextC->ContextInterface(); 64 65 if (pStrategyA) delete pStrategyA; 66 if (pStrategyB) delete pStrategyB; 67 if (pStrategyC) delete pStrategyC; 68 69 if (pContextA) delete pContextA; 70 if (pContextB) delete pContextB; 71 if (pContextC) delete pContextC; 72 }
在实际操作的过程中,我们会发现,在main函数中,也就是在客户端使用策略模式时,会创建非常多的Strategy,而这样就莫名的增加了客户端的压力,让客户端的复杂度陡然增加了。那么,我们就可以借鉴简单工厂模式,使策略模式和简单工厂模式相结合,从而减轻客户端的压力,代码实现如下:
1 #include <iostream> 2 using namespace std; 3 4 // Define the strategy type 5 typedef enum StrategyType 6 { 7 StrategyA, 8 StrategyB, 9 StrategyC 10 }STRATEGYTYPE; 11 12 // The abstract strategy 13 class Strategy 14 { 15 public: 16 virtual void AlgorithmInterface() = 0; 17 virtual ~Strategy() = 0; // 谢谢hellowei提出的bug,具体可以参见评论 18 }; 19 20 Strategy::~Strategy() 21 {} 22 23 class ConcreteStrategyA : public Strategy 24 { 25 public: 26 void AlgorithmInterface() 27 { 28 cout << "I am from ConcreteStrategyA." << endl; 29 } 30 31 ~ConcreteStrategyA(){} 32 }; 33 34 class ConcreteStrategyB : public Strategy 35 { 36 public: 37 void AlgorithmInterface() 38 { 39 cout << "I am from ConcreteStrategyB." << endl; 40 } 41 42 ~ConcreteStrategyB(){} 43 }; 44 45 class ConcreteStrategyC : public Strategy 46 { 47 public: 48 void AlgorithmInterface() 49 { 50 cout << "I am from ConcreteStrategyC." << endl; 51 } 52 53 ~ConcreteStrategyC(){} 54 }; 55 56 class Context 57 { 58 public: 59 Context(STRATEGYTYPE strategyType) 60 { 61 switch (strategyType) 62 { 63 case StrategyA: 64 pStrategy = new ConcreteStrategyA; 65 break; 66 67 case StrategyB: 68 pStrategy = new ConcreteStrategyB; 69 break; 70 71 case StrategyC: 72 pStrategy = new ConcreteStrategyC; 73 break; 74 75 default: 76 break; 77 } 78 } 79 80 ~Context() 81 { 82 if (pStrategy) delete pStrategy; 83 } 84 85 void ContextInterface() 86 { 87 if (pStrategy) 88 pStrategy->AlgorithmInterface(); 89 } 90 91 private: 92 Strategy *pStrategy; 93 }; 94 95 int main() 96 { 97 Context *pContext = new Context(StrategyA); 98 pContext->ContextInterface(); 99 100 if (pContext) delete pContext; 101 }