• 【设计模式】策略模式


    1、定义

    1.1标准定义

      策略模式(StrategyPattern)是一种比较简单的模式,也叫做政策模式(PolicyPattern)。其定义如下:

      Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)

    1.2通用类图

      策略模式使用的就是面向对象的继承和多态机制,非常容易理解和掌握,我们再来看看策略模式的三个角色:

      ●Context封装角色
      它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。

      ●Strategy抽象策略角色
      策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。各位看官可能要问了,类图中的AlgorithmInterface是什么意思,嘿嘿,algorithm运算法则的意思,结合起来意思就明白了吧。

      ●ConcreteStrategy具体策略角色
      实现抽象策略中的操作,该类含有具体的算法。

    2、实现

    2.1类图

      Strategy模式将逻辑(算法)封装到一个类(Context)里面,通过组合的方式将具体算法的实现在组合对象中实现,再通过委托的方式将抽象接口的实现委托给组合对象实现。将算法的逻辑抽象接口(DoAction)封装到一个类中(Context),再通过委托的方式将具体的算法实现委托给具体的Strategy类来实现(ConcreteStrategeA类)。

      Stragegy类,定义所有支持的算法的公共接口。
      ConcreteStrategy类,封装了具体的算法或行为,继承于Strategy。
      Context类,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。

    2.2代码

    2.2.1策略类

    //Strategy.h
    
    #ifndef _STRATEGY_H_
    #define _STRATEGY_H_
    
    class Strategy
    {
    public:
      ~Strategy();
      virtual void AlgrithmInterface() = 0;
    protected:
      Strategy();
    private:
    };
    
    class ConcreteStrategyA: public Strategy
    {
    public:
      ConcreteStrategyA();
      ~ConcreteStrategyA();
      virtual void AlgrithmInterface();
    protected:
    private:
    };
    
    class ConcreteStrategyB: public Strategy
    {
    public:
      ConcreteStrategyB();
      ~ConcreteStrategyB();
      virtual void AlgrithmInterface();
    protected:
    private:
    };
    
    /*这个类是Strategy模式的关键,
    也是Strategy模式和Template模式的根本区别所在。
    *Strategy通过“组合”(委托)方式实现算法(实现)的异构,
    而Template模式则采取的是继承的方式
    这两个模式的区别也是继承和组合两种实现接口重用的方式的区别
    */
    class Context
    {
    public:
      Context(Strategy* );
      ~Context();
      void DoAction();
    private:
      Strategy *_strategy;
    };
    
    #endif
    //Strategy.cpp
    
    #include "Strategy.h"
    #include "iostream"
    
    usingnamespace std;
    
    Strategy::Strategy(){}
    
    Strategy::~Strategy(){}
    
    ConcreteStrategyA::ConcreteStrategyA(){}
    
    ConcreteStrategyA::~ConcreteStrategyA(){}
    
    void ConcreteStrategyA::AlgrithmInterface()
    {
      cout << "ConcreteStrategyA::AlgrithmInterface" << endl;
    }
    
    ConcreteStrategyB::ConcreteStrategyB(){}
    
    ConcreteStrategyB::~ConcreteStrategyB(){}
    
    void ConcreteStrategyB::AlgrithmInterface()
    {
      cout << "ConcreteStrategyB::AlgrithmInterface" << endl;
    }
    
    Context::Context(Strategy *strategy)
    {
      this->_strategy = strategy;
    }
    
    Context::~Context()
    {
      delete this->_strategy;
    }
    
    void Context::DoAction()
    {
      this->_strategy->AlgrithmInterface();
    }

    2.2.3调用

    // main.cpp
    
    #include "Strategy.h"
    
    int main()
    {
    /*
    Strategy模式和Template模式实际是实现一个抽象接口的两种方式:继承和组合之间的区别。
    要实现一个抽象接口,继承是一种方式:我们将抽象接口声明在基类中,将具体的实现放在具体子类中。
    组合(委托)是另外一种方式:我们将接口的实现放在被组合对象中,将抽象接口放在组合类中。
    这两种方式各有优缺点
    */
    //策略A与B可替换
      Strategy *pStr = new ConcreteStrategyA();
      Context *pcon = new Context(pStr);
      pcon->DoAction();
    
      pStr = new ConcreteStrategyB();
      pcon = new Context(pStr);
      pcon->DoAction();
    
      return 0;
    }

    3、优缺点

    3.1优点

      ●算法可以自由切换
      这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对其进行封装,保证对外提供可自由切换的策略。

      ●避免使用多重条件判断
      如果没有策略模式,我们想想看会是什么样子?一个策略家族有5个策略算法,一会要使用A策略,一会要使用B策略,怎么设计呢?使用多重的条件语句?多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。

      ●扩展性良好
      这甚至都不用说是它的优点,因为它太明显了。在现有的系统中增加一个策略太容易了,只要实现接口就可以了,其他都不用修改,类似于一个可反复拆卸的插件,这大大地符合了OCP原则。

    3.2缺点

      ●策略类数量增多
      每一个策略都是一个类,复用的可能性很小,类数量增多。

      ●所有的策略类都需要对外暴露
      上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。

    4、应用场景

      ●多个类只有在算法或行为上稍有不同的场景。

      ●算法需要自由切换的场景。
      例如,算法的选择是由使用者决定的,或者算法始终在进化,特别是一些站在技术前沿的行业,连业务专家都无法给你保证这样的系统规则能够存在多长时间,在这种情况下策略模式是你最好的助手。

      ●需要屏蔽算法规则的场景。
      现在的科技发展得很快,人脑的记忆是有限的(就目前来说是有限的),太多的算法你只要知道一个名字就可以了,传递相关的数字进来,反馈一个运算结果,万事大吉。

  • 相关阅读:
    redis 1 简单介绍和存储的数据结构
    mysql 14 覆盖索引+回表
    mysql 13 B+tree中存储数据的格式 页
    java Arrays.asList() 数组转集合
    java 迭代器
    mysql 12 SQL优化策略
    mysql 11 执行计划
    mysql 10 索引面试题分享
    搭建一个开源项目2-打造另一个环境以及解决上期问题
    搭建一个开源项目1-如何搭建Linux虚拟机
  • 原文地址:https://www.cnblogs.com/ChinaHook/p/7252980.html
Copyright © 2020-2023  润新知