设计模式之策略模式
近期看同事写的代码,看的过程中很是吃力。原因是代码中用了几种设计模式,而我对常用的几种设计模式不熟悉,所以导致看别人调来调去的代码很费劲。正好这几天工作不是特别忙,抓紧时间恶补下相关知识(我看的是《大话设计模式》这本书)。
今天介绍的是策略模式,分为如下四个部分分析。
一、什么是策略模式
策略模式定义了算法家族,分别封装起来,使它们之间可以相互替换,当算法发生变化时,不会影响调用该算法的外部客户。
策略模式的UML类图
二、策略模式用于解决什么问题
只要在分析问题中发现只要在不同时间应用不同的业务规则,就可以使用策略模式解决这种变化性。
三、策略模式的优缺点
优点:
1. 简化了单元测试,每个算法都有自己的类,可以通过自己的接口单独测试。
2. 避免使用多重条件判断语句,易于扩展。
缺点:
1. 每种具体的策略会增加一个新类,从而会增加系统需要维护的类的数量。
2. 需要由客户端选择具体实现的对象,并将其转给Strategy。
四、适用场景
1. 几个类的大部分算法相同,只有部分逻辑算法有所改动。
2. 有相似的行为,需要动态的由客户端决定使用哪一种。
五、举例说明
以商场打折为例:
首先给出一个父类和三个子类的具体代码:
1 //父类 2 class CashSuper 3 { 4 public: 5 CashSuper(){} 6 ~CashSuper(){} 7 virtual double acceptCash(double money); 8 9 }; 10 11 double CashSuper::acceptCash(double money) 12 { 13 cout<<"CashSuper"<<endl; 14 return 0.0; 15 } 16 17 //子类 18 class CashNormal : public CashSuper 19 { 20 public: 21 CashNormal() 22 { 23 cout<<"CashNormal 构造函数.."<<endl; 24 } 25 ~CashNormal() 26 { 27 cout<<"CashNormal 析构函数.."<<endl; 28 } 29 double acceptCash(double money) 30 { 31 return money; 32 } 33 }; 34 35 //子类 36 class CashRabeta:public CashSuper 37 { 38 public: 39 CashRabeta(double mRabeta) 40 { 41 cout<<"CashRabeta 构造函数.."<<endl; 42 rabeta = mRabeta; 43 } 44 ~CashRabeta() 45 { 46 cout<<"CashRabeta 析构函数.."<<endl; 47 } 48 double acceptCash(double money) 49 { 50 return money * rabeta; 51 } 52 private: 53 double rabeta; 54 }; 55 56 //子类 57 class CashReturn:public CashSuper 58 { 59 public: 60 CashReturn(double mMoneyCondition,double mMoneyReturn) 61 { 62 cout<<"CashReturn 构造函数.."<<endl; 63 moneyCondition = mMoneyCondition; 64 moneyReturn = mMoneyReturn; 65 } 66 ~CashReturn() 67 { 68 cout<<"CashReturn 析构函数.."<<endl; 69 } 70 double acceptCash(double money) 71 { 72 if (money >= moneyCondition) 73 { 74 double retain = money - moneyCondition; 75 double preferent = moneyCondition - moneyReturn; 76 money = retain + preferent; 77 } 78 79 return money; 80 } 81 private: 82 double moneyCondition,moneyReturn; 83 };
然后给出Context的代码:
1 class CashContext 2 { 3 public: 4 CashContext(CashSuper *mCS) 5 { 6 cs = mCS; 7 } 8 ~CashContext() 9 { 10 delete cs; 11 } 12 13 double getResult(double money) 14 { 15 if (cs == NULL) 16 { 17 cout<<"cs is NULL"<<endl; 18 } 19 return cs->acceptCash(money); 20 } 21 private: 22 CashSuper *cs; 23 24 };
客户端调用的代码:
1 int main(int argc, char const *argv[]) 2 { 3 cout<<"Strategy .."<<endl; 4 5 int type = 2; 6 double money = 300; 7 8 CashContext *context; 9 switch(type) 10 { 11 case eCashNormal: 12 context = new CashContext(new CashNormal()); 13 break; 14 case eCashRabeta: 15 context = new CashContext(new CashRabeta(0.8)); 16 break; 17 18 case eCashReturn: 19 context = new CashContext(new CashReturn(300,100)); 20 break; 21 } 22 double result = context->getResult(money); 23 24 cout<<"result : "<<result<<endl; 25 26 return 0; 27 }
从上面可以看出客户端决定了使用哪一种算法的对象,如果增添新的优惠活动时,对应的需要修改客户端的代码,这不能很好的遵循“开闭原则”。
我们可以将策略模式和简单工厂模式结合使用,代码如下:
1 //策略模式和简单工厂模式结合 2 class StrategyFactory 3 { 4 public: 5 StrategyFactory(int type) 6 { 7 switch(type) 8 { 9 case eCashNormal: 10 cs = new CashNormal(); 11 break; 12 case eCashRabeta: 13 cs = new CashRabeta(0.8); 14 break; 15 16 case eCashReturn: 17 cs = new CashReturn(300,100); 18 break; 19 } 20 } 21 ~StrategyFactory(){ 22 delete cs; 23 } 24 25 double getResult(double money) 26 { 27 cout<<"strategy getResult.."<<endl; 28 return cs->acceptCash(money); 29 } 30 private: 31 CashSuper *cs; 32 33 }; 34 35 int main(int argc, char const *argv[]) 36 { 37 cout<<"Strategy .."<<endl; 38 39 int type = 2; 40 double money = 300; 41 42 //策略模式和简单工厂模式结合使用 43 StrategyFactory *factory = new StrategyFactory(type); 44 double result = factory->getResult(money); 45 46 cout<<"result : "<<result<<endl; 47 48 return 0; 49 }
注意:简单工厂模式与策略模式之间的区别很小,都是使用了多态和继承。
简单工厂模式只是解决了对象的创建问题,而策略模式的目的是确定使用哪种策略。
举个简单案例:
简单工厂模式:去餐厅点餐,你点了一份油焖大虾,作为用户,你不需要知道这道菜是用了多少油,多少盐等等,你只需要等上几分钟就可得到一份美味的油焖大虾。
策略模式:同样去餐厅点了一份油焖大虾,老板让你自己动手去做。这时候就要自己考虑用多少只虾,多少油,多少盐等,该按照怎么的流程做出最终的油焖大虾。