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


    设计模式介绍

    一、策略模式

    1. 策略模式定义

    定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

    2. 策略模式本质

    分离算法,选择实现
    策略算法是相同行为的不同实现

    3. 策略模式结构和说明

    (1) 结构

    在这里插入图片描述
    在这里插入图片描述

    (2) 调用顺序

    在这里插入图片描述

    在这里插入图片描述

    4. 策略模式适用情况

    1. 出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换。
    2. 出现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。
    3. 需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构。
    4. 出现抽象一个定义了很多行为的类,并且是通过多个if-lse语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。

    5. 策略模式优缺点

    (1) 优点
    1. 定义一系列算法
      策略模式的功能就是定义一系列算法,实现让这些算法可以相互替换。所以会为这一系列算法定义公共的接口,以约束一系列算法要实现的功能。如果这一系列算法具有公共功能,可以把策略接口实现成为抽象类,把这些公共功能实现到父类中,对于这个问题,前面讲了三种处理方法,这里就不再啰嗦了。
    2. 避免多重条件语句
      根据前面的示例会发现,策略模式的一系列策略算法是平等的,是可以互换的,写在一起就是通过if-else结构来组织,如果此时具体的算法实现中又有条件语句,就构成了多重条件语句,使用策略模式能避免这样的多重条件语句。
    3. 更好的扩展性
      在策略模式中扩展新的策略实现非常容易,只要增加新的策略实现类,然后在使用策略的地方选择使用这个新的策略实现就可以了。
    (2) 缺点
    1. 客户必须了解每种策略的不同
      策略模式也有缺点,比如让客户端来选择具体使用哪一个策略,这就需要客户了解所有的策略,还要了解各种策略的功能和不同,这样才能做出正确的选择,而且这样也暴露了策略的具体实现。
    2. 增加了对象数目
      由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。
    3. 只适合扁平的算法结构
      策略模式的一系列算法地位是平等的,是可以相互替换的,事实上构成了一个扁平的算法结构,也就是在一个策略接口下,有多个平等的策略算法,就相当于兄弟算法。而且在运行时刻只有一个算法被使用,这就限制了算法使用的层级,使用的时候不能嵌套使用。

    6. 相关模式

    1. 策略模式和状态模式
      这两个模式从模式结构上看是一样的,但是实现的功能却是不一样的。
      状态模式是根据状态的变化来选择相应的行为,不同的状态对应不同的类,每个状态对应的类实现了该状态对应的功能,在实现功能的同时,还会维护状态数据的变化。这些实现状态对应的功能的类之间是不能相互替换的。策略模式是根据需要或者是客户端的要求来选择相应的实现类,各个实现类是平等的,是可以相互替换的。另外策略模式可以让客户端来选择需要使用的策略算法;而状态模式一般是由上下文,或者是在状态实现类里面来维护具体的状态数据,通常不由客户端来指定状态。
    2. 策略模式和模板方法模式
      这两个模式可组合使用,如同前面示例的那样。
      模板方法重在封装算法骨架:而策略模式重在分离并封装算法实现
    3. 策略模式和享元模式
      这两个模式可组合使用。
      策略模式分离并封装出一系列的策略算法对象,这些对象的功能通常都比较单一,很多时候就是为了实现某个算法的功能而存在。因此,针对这一系列的、多个细粒度的对象,可以应用享元模式来节省资源,但前提是这些算法对象要被频繁地使用,如果偶尔用一次,就没有必要做成享元了。

    策略模式示例代码

    //示例1
    #include 
    #include 
    #include 
    
    using namespace std;
    
    //高层策略接口
    class Strategy
    {
    public:
        virtual double CalcPrice(double  goodsPrice){return 0;}
    };
    
    //具体策略
    //普通客户策略
    class NormalCustomerStrategy : public Strategy
    {
    public:
        double CalcPrice(double  goodsPrice) override
        {
            //普通客户没有折扣
            std::cout<<"普通客户没有折扣"<CalcPrice(goodsPrice);
            }
            return 0;
        }
    private:
        std::unique_ptr pStrategy {nullptr};
    };
    int main()
    {
        {
            Price price(new NormalCustomerStrategy);
            double goodsPrice = price.Quote(100);
            std::cout<<"普通客户最终价:"<
    • 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
    //示例2
    #include 
    #include 
    #include 
    
    using namespace std;
    
    //策略模式扩展方式
    //1.扩展上下文:通过继承上下文方式,然后在子类中添加相关数据
    //2.扩展策略算法:在具体策略算法中添加相关数据
    
    //高层策略接口
    class PayStrategy
    {
    public:
        //virtual void Pay(PayContext* pPayContext){} //c++ 没有反射 不能直接传入context 然后获取上下文相关数据 适用于扩展方式1
        virtual void Pay(const std::string& user, double money){}
    };
    
    //具体策略
    //人民币现金支付
    class RMBCashStrategy : public PayStrategy
    {
    public:
        void Pay(const std::string& user, double money) override
        {
            std::cout<Pay(this->user, this->money);
            }
        }
    
    private:
        std::unique_ptr pPayStrategy {nullptr};
    };
    
    
    int main()
    {
        {
            PayContext* payContext = new PayContext("张三", 100.0, new RMBCashStrategy());
            payContext->PayNow();
        }
    
        {
            PayContext* payContext = new PayContext("petter", 200.0, new DollarCashStrategy());
            payContext->PayNow();
        }
    
        {
            PayContext* payContext = new PayContext("李四", 300.0, new RMBAccountStrategy("123456789"));
            payContext->PayNow();
        }
        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
  • 相关阅读:
    数据结构与算法【Java】08---树结构的实际应用
    野火FPGA强化(1):串口
    力扣:1143.最长公共子序列
    程序通过命令行获取操作系统名称+版本+CPU名称等:Part2
    2. Vue3 Composition API
    vue里面的v-for循环给v-model赋值问题
    Flutter学习笔记 -- 常量与变量、内置类型
    为什么要使用双重校验锁来实现单例模式?
    【我的OpenGL学习进阶之旅】关于OpenGL的着色器的向量和矩阵分类的访问方式: xyzw/rgba/stpq以及数组下标
    GEO生信数据挖掘(四)数据清洗(离群值处理、低表达基因、归一化、log2处理)
  • 原文地址:https://blog.csdn.net/wangdamingll/article/details/128209493