• C++设计模式02-——策略设计模式


    C++设计模式02-——策略设计模式

    一、策略模式的定义:

        定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)
     

    二、快速理解策略模式:

    1. 概述理解

        策略 ——普通语境下,是我们对一件事情的处理方法,由于对不同的事情我们有不同的应对方式,我们通过权衡利弊,得到合适的方案,这个过程就是挑选策略。
        在面向对象编程中,对于某类事务,有着不同的处理方式,将这些方式向上抽象一层就有了策略模式。这里并不是说一般的处理方式不能完成任务,而是我们的代码是面对变化的需求的,我们要让代码简单方便维护,所以才需要此类设计模式。
     

    2. 问题场景

        比如,对于早餐来说,想吃馒头、包子、吃油条、吃面包…就要自己买或者自己做这些食物,我们把得到某食物的过程称为做(make)某食物(无论是自己做还是买),早餐吃什么完全取决于自己的想法,想吃啥就要有人做它,现在需求是根据自己的想法显示主食的制作过程,有伪代码如下:

    enum StapleFood{ // 主食
        ManTou, // 馒头
        BaoZi, // 包子
        YouTiao, // 油条
        MianBao, // 面包
    };
    
    class Breakfast {// 早餐类
    private:
    	StapleFood staplefood;
    public:
        Breakfast() {}
    	Breakfast(StapleFood stfood) : staplefood(stfood) {}
     	void setStapleFood(StapleFood stfood) {
     		staplefood = stfood;
     	}
     	void MakeFoodInfo() {
     	    if (staplefood == Mantou) {
     	    	cout << "正在蒸馒头" << endl;
     	    	cout << "......" << endl; // 蒸馒头一系列复杂操作
     	    } else if (staplefood == Baozi ) {
     	    	cout << "正在做包子:" << endl;
     	    	cout << "......" << endl; // 做包子一系列操作
     	    } else if (staplefood == YouTiao) {
     	    	cout << "正在炸油条:" << endl;
     	    	cout << "......" << endl; // 炸油条一系列操作
     	    }
     	}
    };
    
    • 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

        当主食随着时代发展,可能出现几十上百种食物,它们制作过程各不相同,而且工艺复杂,想要增加一种早餐主食,至少要修改:

    1. 枚举类型:主食
    enum StapleFood{ // 主食
        ManTou, // 馒头
        BaoZi, // 包子
        YouTiao, // 油条
        MianBao, // 面包
        添加主食类型
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 函数MakeFoodInfo:制作过程信息
    void MakeFoodInfo() {
     	    if (staplefood == Mantou) {
     	    	cout << "正在蒸馒头" << endl;
     	    	cout << "......" << endl; // 蒸馒头一系列复杂操作
     	    } else if (staplefood == Baozi ) {
     	    	cout << "正在做包子:" << endl;
     	    	cout << "......" << endl; // 做包子一系列操作
     	    } else if (staplefood == YouTiao) {
     	    	cout << "正在炸油条:" << endl;
     	    	cout << "......" << endl; // 炸油条一系列操作
     	    } else if(staplefood == 添加的主食) {
     	        添加制作该食物的过程
     	    }
     	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    严重违反开闭原则! ,因为一旦添加一种食物,就要在原先的结构上修修改改,这样很容易出错,策略模式应运而生。
     

    3. 策略模式

        策略模式的做法很简单,也就是我在上面所说的,将这些方式向上抽象一层,利用多态的特性,在C++中让子类继承抽象基类,在java中还可以通过实现抽象接口的形式操作(后面会讲到)。
        沿承上面的早餐的例子,对于主食我们完全可以做一个抽象,如下:

    class StapleFood {
    public:
      	StapleFood() {}
      	virtual void MakeFoodInfo() = 0;
      	virtual ~StapleFood() {}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    此时,主食是一个抽象类,因为其含有纯虚函数。
    对于目前的主食信息,有:

    class MaTou : public StapleFood 
    {
      	virtual void MakeFoodInfo() {
      		cout << "正在蒸馒头" << endl;
          	cout << "......" << endl; // 蒸馒头一系列复杂操作
      	}
    };
    class  BaoZi  : public StapleFood 
    {
      	virtual void MakeFoodInfo() {
      		cout << "正在做包子:" << endl;
          	cout << "......" << endl; // 做包子一系列操作
        }
    };
    class YouTiao : public StapleFood  
    {
    	virtual void MakeFoodInfo() {
    	   cout << "正在炸油条:" << endl;
           cout << "......" << endl; // 炸油条一系列操作
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这时候把所有美食的制作都交给厨师统一管理:

    class Cook {
    private:
    	StapleFood* staplefood;
    public:
    	Cook() {}
        Cook(StapleFood* stfood) : staplefood(stfood) {}
        void setStapleFood(StapleFood* stfood) {
        	staplefood = stfood;
        }
        void MakeFoodInfo() {
           if (staplefood == nullptr) {
           		cout << "厨师没有做饭——饿着吧(谁让你没告诉厨师想吃什么)" << endl;
           		return;
           }
           staplefood->MakeFoodInfo();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    如果早餐想吃包子,就这样告诉厨师:

    int main() {
    	Cook* cook = new Cook();// new (找)一个厨师
    	cook->setStapleFood(new BaoZi()); // 告诉厨师想吃包子
    	cook->MakeFoodInfo(); // 厨师开始做包子
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    也可以, new出厨师的同时指定厨师做某种食物(现实意义就是为了吃某种食物专门聘请厨师)

    int main() {
    	BaoZi* baozi = new BaoZi(); // 想吃包子了
    	Cook *cook = new Cook(baozi);// 找一个超级会做包子的厨师
    	cook->MakeFoodInfo(); // 厨师开始做包子
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

     
    这个时候当主食随着时代发展,比如推出了新的主食——奥力给,拓展只要新建文件:

    class AoLiGei : public StapleFood  
    {
    	virtual void MakeFoodInfo() {
    	   cout << "正在制作奥里给:" << endl;
           cout << "......" << endl; // make奥里给一系列操作
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这样,并没有修改旧的文件,这里说明一下,一般开发,一个类是放在一个.cpp和.h文件里面的,.h为类的声明,.cpp里面是对类的方法的实现(不考虑内联类,上面代码均为内联而且一个文件里面出现多个类,只是为了方便),我们这种做法(指策略模式),并没有改变旧文件,而是添加了新文件,根本不会对以前的业务造成影响,拓展性极强!
     
    这个时候走进来一个美食家: ——嗨嗨嗨!虽然不是同一个时间,但是在同一个撤硕

    int main() {
    	AoLiGei* aoligei = new AoLiGei(); // 想吃***了
    	Cook *cook = new Cook(baozi);// 不用厨子了,自产自销————指自己做自己吃(杆菌又卫生)
    	cook->MakeFoodInfo(); // making 造它就完了!干了~奥里给!
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      

    三、策略模式案例

    1. 不同国家税收问题(极客班)

    代码被我调试过,和老师讲课的时候略有不同,老师上课用的伪代码

    初始代码:

    // test02.cpp
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    enum TaxBase{
    	CN_Tax,
    	US_Tax,
    	DE_Tax,
    	FR_Tax
    };
    
    class SalesOrder {
    	TaxBase tax;
    public:
        void setTaxType(TaxBase tax) {
        	this->tax = tax;
        }
        double CalculateTax() {
       	   double val = 0.0;
            //...
           if (tax == CN_Tax) {
           	  	return 1.0;
           } else if (tax == US_Tax) {
           		return 2.0;
           } else if (tax == DE_Tax) {
           		return 3.0;
           } else if (tax == FR_Tax) {
           		return 4.0;
           }
            //...
            return val;
        }
    };
    
    int main() {
    
    	SalesOrder* salesOrder = new SalesOrder();
    	salesOrder->setTaxType(US_Tax);
    
    	cout << salesOrder->CalculateTax() << endl;
    
    	return 0;
    }
    
    • 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

    改进代码:

    // test01.cpp
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Context {
    public:
    	Context() {}
    };
    
    class TaxStrategy {
    public:
        virtual double Calculate(const Context& context) = 0;
        virtual ~TaxStrategy() {}
    };
    
    class CNTax : public TaxStrategy {
    public:
        virtual double Calculate(const Context& context) {
        	double res = 0.0;
            //***********
            return res;
        }
    };
    
    class USTax : public TaxStrategy {
    public:
        virtual double Calculate(const Context& context) {
        	double res = 1.0;
            //***********
            return res;
        }
    };
    
    class DETax : public TaxStrategy {
    public:
        virtual double Calculate(const Context& context) {
        	double res = 2.0;
            //***********
            return res;
        }
    };
    
    //扩展
    //*********************************
    class FRTax : public TaxStrategy {
    public:
        virtual double Calculate(const Context& context) {
        	double res = 3.0;
            //.........
            return res;
        }
    };
    
    class StrategyFactory {
    public:
    	virtual TaxStrategy* NewStrategy() = 0;
    	virtual ~StrategyFactory() {}
    };
    
    class CNFactory : public StrategyFactory {
    public:
    	virtual TaxStrategy* NewStrategy() {
    		return new CNTax();
    	}
    };
    
    class USFactory : public StrategyFactory {
    public:
    	virtual TaxStrategy* NewStrategy() {
    		return new USTax();
    	}
    };
    
    class DEFactory : public StrategyFactory {
    public:
    	virtual TaxStrategy* NewStrategy() {
    		return new DETax();
    	}
    };
    
    class FRFactory : public StrategyFactory {
    public:
    	virtual TaxStrategy* NewStrategy() {
    		return new FRTax();
    	}
    };
    
    
    class SalesOrder {
    private:
        TaxStrategy* strategy;
    
    public:
        SalesOrder(StrategyFactory* strategyFactory) {
            this->strategy = strategyFactory->NewStrategy();
        }
        ~SalesOrder() {
            delete this->strategy;
        }
    
       double CalculateTax() {
            //...
            Context context;
            double val = strategy->Calculate(context); //多态调用
            //...
            return val;
       }
    };
    
    int main() {
    
    	StrategyFactory* strategy = new USFactory();//CNFactory();
    
    	SalesOrder* salesOrder = new SalesOrder(strategy);
    
    	cout << salesOrder->CalculateTax() << endl;
    
    	return 0;
    }
    
    • 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
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128

    代码来自网络——如有侵权联系我删除

    2. 不同鸭子的问题(尚硅谷)

       点击这个跳转视频,尚硅谷-设计模式-策略模式,韩老师用的eclipse,java示例,视频很清楚代码如何写的,自己动手丰衣足食。
     

    上面两个包括我举得例子思想都是一致的,就没有具体分析了,另外说明,尚硅谷韩老师在讲解这个例子的时候用的是java写的,使用了接口,而李老师使用C++讲解没有用接口(C++好像没接口),使用的继承,但是都是一个思想,我思考了一些时间后得出的结论。


    THE END…

  • 相关阅读:
    活动回顾∣企企通亮相高质量企业数字化活动,深入探讨各领域采购数字化转型与变革
    conda环境下version libcublasLt.so.11 not defined问题解决
    [搞点好玩的] JETSONNANO 受苦记 -- 001 (布置环境,未完待续)
    SpringBoot 整合 Minio 实现 文件上传
    FANUC机器人电气控制柜内部硬件电路和模块详细介绍
    论文基础常识摘录
    windows文件和目录相关命令
    手机怎么把照片转JPG格式?这三种手机小技巧需要知道
    【selenium】复用浏览器(debugger)
    AutoSAR入门:开发工具链介绍
  • 原文地址:https://blog.csdn.net/qq_51340322/article/details/126289191