[设计模式] 设计模式课程(三)--策略模式

概述

  • 属于行为型模式
  • 定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换
  • 找出负责用许多不同方法完成特殊任务的类,然后将其中的算法抽取到一组被称为策略的独立类中

结构

  • 上下文类:维护指向具体策略的引用,仅通过策略接口与该对象进行交流
  • 策略接口:所有具体策略的通用接口,声明了一个上下文用于执行策略的方法
  • 具体策略:实现了上下文所用算法的各种不同变体
  • 客户端:创建一个特定策略对象并将其传递给上下文,上下文提供一个设置器以便客户端在运行时替换相关策略

场景

  • 假如你要前往机场,可以选择乘坐公共汽车,预约出租车或骑自行车,可根据预算或时间等因素选择其中一种策略

示例1

 1 enum TaxBase {
 2     CN_Tax,
 3     US_Tax,
 4     DE_Tax,
 5     FR_Tax       //更改
 6 };
 7 
 8 class SalesOrder{
 9     TaxBase tax;
10 public:
11     double CalculateTax(){
12         //...
13         
14         if (tax == CN_Tax){
15             //CN***********
16         }
17         else if (tax == US_Tax){
18             //US***********
19         }
20         else if (tax == DE_Tax){
21             //DE***********
22         }
23         else if (tax == FR_Tax){  //更改
24             //...
25         }
26 
27         //....
28      }
29     
30 };
View Code
  • 静态看没有问题,动态看有问题,比如后期还要支持日本、法国等其他国家
  • 更改代码的过程,违反了开闭原则,考虑重构,用扩展的方式应对变化
 1 class TaxStrategy{
 2 public:
 3     virtual double Calculate(const Context& context)=0;
 4     virtual ~TaxStrategy(){}
 5 };
 6 
 7 
 8 class CNTax : public TaxStrategy{
 9 public:
10     virtual double Calculate(const Context& context){
11         //***********
12     }
13 };
14 
15 class USTax : public TaxStrategy{
16 public:
17     virtual double Calculate(const Context& context){
18         //***********
19     }
20 };
21 
22 class DETax : public TaxStrategy{
23 public:
24     virtual double Calculate(const Context& context){
25         //***********
26     }
27 };
28 
29 
30 
31 //扩展
32 //*********************************
33 class FRTax : public TaxStrategy{
34 public:
35     virtual double Calculate(const Context& context){
36         //.........
37     }
38 };
39 
40 
41 class SalesOrder{
42 private:
43     TaxStrategy* strategy;
44 
45 public:
46     SalesOrder(StrategyFactory* strategyFactory){
47         this->strategy = strategyFactory->NewStrategy();
48     }
49     ~SalesOrder(){
50         delete this->strategy;
51     }
52 
53     public double CalculateTax(){
54         //...
55         Context context();
56         
57         double val = 
58             strategy->Calculate(context); //多态调用
59         //...
60     }
61     
62 };
View Code
  • 实现多态性,必须要靠指针,而不是具体对象
  • 42销售类,44策略指针,支持多态性
  • 48采用工厂模式创建实例,因为是堆对象,要写虚构函数47,56多态调用
  • 使类型在运行时根据需要在各个算法间切换(41, 56)
  • SalesOrder 类全程不变,实现了复用,后期可通过扩展子类实现变化

示例2

商场促销策略

  • 简单工厂模式实现
 1 #include <iostream>
 2 #include <math.h>
 3 
 4 using namespace std;
 5 
 6 class CashSuper{
 7     public:
 8         virtual double acceptCash(double money) = 0;
 9 };
10 // 正常收费类 
11 class CashNormal:public CashSuper{
12     public:
13         double acceptCash(double money){
14             return money;
15         }
16 };
17 // 返利收费类 
18 class CashReturn:public CashSuper{
19     private:
20         double moneyCondition;
21         double moneyReturn;
22     public:
23         CashReturn(double moneyCondition, double moneyReturn){
24             this->moneyCondition = moneyCondition;
25             this->moneyReturn = moneyReturn;
26         }
27         double acceptCash(double money){
28             double result = money;
29             if(moneyCondition)
30                 result = money - floor(money/moneyCondition)*moneyReturn;
31             return result;
32         }
33 }; 
34 // 打折收费类
35 class CashRebate:public CashSuper{
36     private:
37         double moneyRebate;
38     public:
39         CashRebate(double moneyRebate){
40             this->moneyRebate = moneyRebate;
41         }
42         double acceptCash(double money){
43             return money*moneyRebate;
44         }
45 };
46 // 工厂类
47 class CashFactory{
48     public:
49         static CashSuper* Create(int type){
50             CashSuper* cs;
51             switch(type){
52                 case 1:{
53                     cs = new CashNormal();
54                     break;
55                 }
56                 case 2:{
57                     cs = new CashReturn(300, 100);
58                     break;
59                 }
60                 case 3:{
61                     cs = new CashRebate(0.8);
62                     break;
63                 }
64                 default:;
65             }
66         return cs;
67         }
68 }; 
69 
70 int main(){
71     double total = 0;
72     double totalPrice = 0;
73     
74     CashSuper* cc1 = CashFactory::Create(1);
75     totalPrice = cc1->acceptCash(300);
76     total += totalPrice;
77     cout << "Type:正常收费 totalPrices:" << totalPrice << " total:" 
78     << total << endl;
79     totalPrice = 0;
80     
81     CashSuper* cc2 = CashFactory::Create(2);
82     totalPrice = cc2->acceptCash(300);
83     total += totalPrice;
84     cout << "Type:正常收费 totalPrices:" << totalPrice << " total:" 
85     << total << endl;
86     totalPrice = 0;
87 
88     CashSuper* cc3 = CashFactory::Create(3);
89     totalPrice = cc3->acceptCash(300);
90     total += totalPrice;
91     cout << "Type:正常收费 totalPrices:" << totalPrice << " total:" 
92     << total << endl;
93     totalPrice = 0;
94     
95     return 0;
96 } 
View Code

  • 策略模式实现
    • 菱形+箭头代表聚合(如雁群和大雁)
  1 #include <iostream>
  2 #include <math.h>
  3 
  4 using namespace std;
  5 
  6 class CashSuper{
  7     public:
  8         virtual double acceptCash(double money) = 0;
  9 };
 10 // 正常收费类 
 11 class CashNormal:public CashSuper{
 12     public:
 13         double acceptCash(double money){
 14             return money;
 15         }
 16 };
 17 // 返利收费类 
 18 class CashReturn:public CashSuper{
 19     private:
 20         double moneyCondition;
 21         double moneyReturn;
 22     public:
 23         CashReturn(double moneyCondition, double moneyReturn){
 24             this->moneyCondition = moneyCondition;
 25             this->moneyReturn = moneyReturn;
 26         }
 27         double acceptCash(double money){
 28             double result = money;
 29             if(moneyCondition)
 30                 result = money - floor(money/moneyCondition)*moneyReturn;
 31             return result;
 32         }
 33 }; 
 34 // 打折收费类
 35 class CashRebate:public CashSuper{
 36     private:
 37         double moneyRebate;
 38     public:
 39         CashRebate(double moneyRebate){
 40             this->moneyRebate = moneyRebate;
 41         }
 42         double acceptCash(double money){
 43             return money*moneyRebate;
 44         }
 45 };
 46 // 策略类
 47 class CashContext{
 48     private:
 49         CashSuper* cs;
 50     public:
 51         CashContext(CashSuper* csuper):cs(NULL){
 52             this->cs = csuper;
 53         }
 54         ~CashContext(){
 55             if(cs != NULL){
 56                 delete cs;
 57                 cs = NULL;
 58             }
 59         }
 60         double GetResult(double money){
 61             return cs->acceptCash(money);
 62         }
 63 }; 
 64 
 65 int main(){
 66     double total = 0;
 67     double totalPrice = 0;
 68     
 69     CashContext* cc1 = NULL;
 70     cc1 = new CashContext(new CashNormal());
 71     totalPrice = cc1->GetResult(300);
 72     total += totalPrice;
 73     cout << "Type:正常收费 totalPrices:" << totalPrice << " total:" 
 74     << total << endl;
 75     totalPrice = 0;
 76     
 77     CashContext* cc2 = NULL;
 78     cc2 = new CashContext(new CashReturn(300,100));
 79     totalPrice = cc2->GetResult(300);
 80     total += totalPrice;
 81     cout << "Type:满300返100 totalPrices:" << totalPrice << " total:" 
 82     << total << endl;
 83     totalPrice = 0;
 84     
 85     CashContext* cc3 = NULL;
 86     cc3 = new CashContext(new CashRebate(0.8));
 87     totalPrice = cc3->GetResult(300);
 88     total += totalPrice;
 89     cout << "Type:打8折 totalPrices:" << totalPrice << " total:" 
 90     << total << endl;
 91     totalPrice = 0;
 92     
 93     if(cc1 != NULL){
 94         delete cc1;
 95         cc1 = NULL; 
 96     }
 97     if(cc2 != NULL){
 98         delete cc2;
 99         cc2 = NULL; 
100     }
101     if(cc3 != NULL){
102         delete cc3;
103         cc3 = NULL; 
104     }
105     return 0;
106 } 
View Code
    • 不足:要在客户端判断用哪种算法(此处写法为示意,实际程序中需要用判断语句决定用哪种算法)
    • 改进:和简单工厂模式结合,把判断过程写入策略类中
  • 改进的策略模式实现
  1 #include <iostream>
  2 #include <math.h>
  3 
  4 using namespace std;
  5 
  6 class CashSuper{
  7     public:
  8         virtual double acceptCash(double money) = 0;
  9 };
 10 // 正常收费类 
 11 class CashNormal:public CashSuper{
 12     public:
 13         double acceptCash(double money){
 14             return money;
 15         }
 16 };
 17 // 返利收费类 
 18 class CashReturn:public CashSuper{
 19     private:
 20         double moneyCondition;
 21         double moneyReturn;
 22     public:
 23         CashReturn(double moneyCondition, double moneyReturn){
 24             this->moneyCondition = moneyCondition;
 25             this->moneyReturn = moneyReturn;
 26         }
 27         double acceptCash(double money){
 28             double result = money;
 29             if(moneyCondition)
 30                 result = money - floor(money/moneyCondition)*moneyReturn;
 31             return result;
 32         }
 33 }; 
 34 // 打折收费类
 35 class CashRebate:public CashSuper{
 36     private:
 37         double moneyRebate;
 38     public:
 39         CashRebate(double moneyRebate){
 40             this->moneyRebate = moneyRebate;
 41         }
 42         double acceptCash(double money){
 43             return money*moneyRebate;
 44         }
 45 };
 46 // 策略类
 47 class CashContext{
 48     private:
 49         CashSuper* cs;
 50     public:
 51         CashContext(int type):cs(NULL){
 52             switch(type){
 53                 case 1:{
 54                     cs = new CashNormal();
 55                     break;
 56                 }
 57                 case 2:{
 58                     cs = new CashReturn(300, 100);
 59                     break;
 60                 }
 61                 case 3:{
 62                     cs = new CashRebate(0.8);
 63                     break;
 64                 }
 65                 default:;
 66             }
 67         }
 68         ~CashContext(){
 69             if(cs != NULL){
 70                 delete cs;
 71                 cs = NULL;
 72             }
 73         }
 74         double GetResult(double money){
 75             return cs->acceptCash(money);
 76         }
 77 }; 
 78 
 79 int main(){
 80     double total = 0;
 81     double totalPrice = 0;
 82     
 83     CashContext* cc1 = NULL;
 84     cc1 = new CashContext(1);
 85     totalPrice = cc1->GetResult(300);
 86     total += totalPrice;
 87     cout << "Type:正常收费 totalPrices:" << totalPrice << " total:" 
 88     << total << endl;
 89     totalPrice = 0;
 90     
 91     CashContext* cc2 = NULL;
 92     cc2 = new CashContext(2);
 93     totalPrice = cc2->GetResult(300);
 94     total += totalPrice;
 95     cout << "Type:满300返100 totalPrices:" << totalPrice << " total:" 
 96     << total << endl;
 97     totalPrice = 0;
 98     
 99     CashContext* cc3 = NULL;
100     cc3 = new CashContext(3);
101     totalPrice = cc3->GetResult(300);
102     total += totalPrice;
103     cout << "Type:打8折 totalPrices:" << totalPrice << " total:" 
104     << total << endl;
105     totalPrice = 0;
106     
107     if(cc1 != NULL){
108         delete cc1;
109         cc1 = NULL; 
110     }
111     if(cc2 != NULL){
112         delete cc2;
113         cc2 = NULL; 
114     }
115     if(cc3 != NULL){
116         delete cc3;
117         cc3 = NULL; 
118     }
119     return 0;
120 } 
View Code

    • 策略模式和简单工厂模式的区别:可直接在客户端接受用户输入判断算法
    • 改进后的策略模式和简单工厂模式的区别:简单工厂模式需要在客户端中用CashSuper和CashFactory两个类,改进的策略模式只要用CashContext即可,降低了耦合

**错误代码**

1 CashSuper csuper = CashFactory.Create(2);
2 totalPrice = csuper.acceptCash(300);
3 total += totalPrice;
4 cout << "Type:满300返100 totalPrices:" << totalPrice << " total:" 
5 << total << endl;
6 totalPrice = 0;
  • 1行,csuper是CashSuper的指针类型,应加*;CashFactory是类名,应用::
  • 2行,csuper是指针变量,应用->

示例3

  1 #include <iostream>
  2 #include <typeinfo>
  3 #include <string>
  4 #include <vector>
  5 #include <algorithm>
  6 /**
  7  * The Strategy interface declares operations common to all supported versions
  8  * of some algorithm.
  9  *
 10  * The Context uses this interface to call the algorithm defined by Concrete
 11  * Strategies.
 12  */
 13 class Strategy
 14 {
 15 public:
 16     virtual ~Strategy() {}
 17     virtual std::string DoAlgorithm(const std::vector<std::string> &data) const = 0;
 18 };
 19 
 20 /**
 21  * The Context defines the interface of interest to clients.
 22  */
 23 
 24 class Context
 25 {
 26     /**
 27      * @var Strategy The Context maintains a reference to one of the Strategy
 28      * objects. The Context does not know the concrete class of a strategy. It
 29      * should work with all strategies via the Strategy interface.
 30      */
 31 private:
 32     Strategy *strategy_;
 33     /**
 34      * Usually, the Context accepts a strategy through the constructor, but also
 35      * provides a setter to change it at runtime.
 36      */
 37 public:
 38     Context(Strategy *strategy = nullptr) : strategy_(strategy)
 39     {
 40     }
 41     ~Context()
 42     {
 43         delete this->strategy_;
 44     }
 45     /**
 46      * Usually, the Context allows replacing a Strategy object at runtime.
 47      */
 48     void set_strategy(Strategy *strategy)
 49     {
 50         delete this->strategy_;
 51         this->strategy_ = strategy;
 52     }
 53     /**
 54      * The Context delegates some work to the Strategy object instead of
 55      * implementing +multiple versions of the algorithm on its own.
 56      */
 57     void DoSomeBusinessLogic() const
 58     {
 59         // ...
 60         std::cout << "Context: Sorting data using the strategy (not sure how it'll do it)
";
 61         std::string result = this->strategy_->DoAlgorithm(std::vector<std::string>{"a", "e", "c", "b", "d"});
 62         std::cout << result << "
";
 63         // ...
 64     }
 65 };
 66 
 67 /**
 68  * Concrete Strategies implement the algorithm while following the base Strategy
 69  * interface. The interface makes them interchangeable in the Context.
 70  */
 71 class ConcreteStrategyA : public Strategy
 72 {
 73 public:
 74     std::string DoAlgorithm(const std::vector<std::string> &data) const override
 75     {
 76         std::string result;
 77         std::for_each(std::begin(data), std::end(data), [&result](const std::string &letter) {
 78             result += letter;
 79         });
 80         std::sort(std::begin(result), std::end(result));
 81 
 82         return result;
 83     }
 84 };
 85 class ConcreteStrategyB : public Strategy
 86 {
 87     std::string DoAlgorithm(const std::vector<std::string> &data) const override
 88     {
 89         std::string result;
 90         std::for_each(std::begin(data), std::end(data), [&result](const std::string &letter) {
 91             result += letter;
 92         });
 93         std::sort(std::begin(result), std::end(result));
 94         for (int i = 0; i < result.size() / 2; i++)
 95         {
 96             std::swap(result[i], result[result.size() - i - 1]);
 97         }
 98 
 99         return result;
100     }
101 };
102 /**
103  * The client code picks a concrete strategy and passes it to the context. The
104  * client should be aware of the differences between strategies in order to make
105  * the right choice.
106  */
107 
108 void ClientCode()
109 {
110     Context *context = new Context(new ConcreteStrategyA);
111     std::cout << "Client: Strategy is set to normal sorting.
";
112     context->DoSomeBusinessLogic();
113     std::cout << "
";
114     std::cout << "Client: Strategy is set to reverse sorting.
";
115     context->set_strategy(new ConcreteStrategyB);
116     context->DoSomeBusinessLogic();
117     delete context;
118 }
119 
120 int main()
121 {
122     ClientCode();
123     return 0;
124 }
View Code

示例4

 1 public interface Strategy {
 2    public int doOperation(int num1, int num2);
 3 }
 4 
 5 public class OperationAdd implements Strategy{
 6    @Override
 7    public int doOperation(int num1, int num2) {
 8       return num1 + num2;
 9    }
10 }
11 
12 public class OperationSubstract implements Strategy{
13    @Override
14    public int doOperation(int num1, int num2) {
15       return num1 - num2;
16    }
17 }
18 
19 public class OperationMultiply implements Strategy{
20    @Override
21    public int doOperation(int num1, int num2) {
22       return num1 * num2;
23    }
24 }
25 
26 public class Context {
27    private Strategy strategy;
28 
29    public Context(Strategy strategy){
30       this.strategy = strategy;
31    }
32 
33    public int executeStrategy(int num1, int num2){
34       return strategy.doOperation(num1, num2);
35    }
36 }
37 
38 public class StrategyPatternDemo {
39    public static void main(String[] args) {
40       Context context = new Context(new OperationAdd());        
41       System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
42 
43       context = new Context(new OperationSubstract());       
44       System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
45 
46       context = new Context(new OperationMultiply());        
47       System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
48    }
49 }
View Code
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

总结

  • 动机:软件构建过程中,某些对象使用的算法多种多样,经常改变
  • 若将这些代码写在对象中,将会使对象变得异常复杂,且支持不用的算法会带来性能负担
  • 如何在运行时根据需要透明地更改对象的算法,将其与对象本身解耦
  • 复用:保证编译后的二进制代码不用改变,不是粘贴代码片段
  • 每次扩展,在新文件中添加一个新类即可
  • 定义一系列算法,把它们封装起来,使它们可以相互替换(变化),使得算法可以独立于使用它的客户程序(稳定)而变化
  • 提供了条件判断语句外的另一种选择,减低了耦合,含有许多条件判断语句的代码通常都需要Stragegy模式
  • 当 if else 稳定不变时则不用,如性别、每周七天等

  • Context指向Strategy,Context通过Strategy接口调用一系列算法,ConcreteStrategyA/B/C实现具体的算法
  • 优点:适合类中的成员以方法为主,算法经常变动的情况;简化了单元测试(每个算法可以通过自己的类接口单独测试)
  • 缺点:需要客户端判断使用哪种算法

参考

https://refactoringguru.cn/design-patterns/strategy

原文地址:https://www.cnblogs.com/cxc1357/p/12249655.html