• 设计模式-策略模式Strategy以及消灭if else


    概述

      如果在开发过程中,出现大量的if else或者switch case 语句,如果这些语句块中的代码并不是包含业务逻辑,只是单纯的分流方法,那么,每一个语句块中都是一个算法或者叫策略

    背景

      比如在最近项目中遇到的问题。一个二维码字符串解析的方法:

        微信的二维码扫描结果包含“WeChat”,解析规则是拿着文本到微信服务器解析,返回解析对象

        支付宝二维码扫描结果包含“Alipay”,解析规则是使用“->”分割字符串得到解析对象。

      最简单快速的代码就是直接if else判断:

      

     1         /// <summary>
     2         /// 解析方法
     3         /// </summary>
     4         /// <param name="text">扫描得到的文本</param>
     5         public void AnalysisAction(string text)
     6         {
     7             //微信解析方法
     8             if (text.Contains("WeChat"))
     9             {
    10                 //拿着text到微信服务器解析,返回解析对象。
    11             }
    12             //支付宝解析方法
    13             else if (text.Contains("Alipay"))
    14             {
    15                 //使用->分割,得到解析对象。
    16             }
    17         }

     问题

      当然使用这种方式是可以的,但是如果以后又加入一种扫码解析方法:

      中国联通二维码扫描文本中包含“Unicom”,解析规则为以“:”分割,得到解析对象。

      那么你就要继续添加else if(text.Contains("Unicom"))。每次增加一个新的扫描解析规则,你都要去增加else if判断,这种是面向过程的体验,属于硬编码。这也违反了面向对象的开闭原则。

    改进(抽象)

      我们可以使用策略模式来改进代码。义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

      抽象出一个扫描解析接口,定义一个解析方法。然后分别定义微信和支付宝的解析方法集成接口。

      策略模式的uml图如下:

      

     
        /// <summary>
        /// 扫描解析规则抽象接口
        /// </summary>
        public interface IStrategy
        {
            /// <summary>
            /// 扫描解析方法策略
            /// </summary>
            /// <param name="text"></param>
            void AnalysisAction(string text);
        }
    扫描解析规则抽象接口
        /// <summary>
        /// 微信扫描解析规则
        /// </summary>
        public class StrategyWeChat : IStrategy
        {
            /// <summary>
            /// 扫描策略
            /// </summary>
            /// <param name="text"></param>
            public void AnalysisAction(string text)
            {
                //拿着text到微信服务器解析,返回解析对象。
            }
        }
    微信扫描策略
        /// <summary>
        /// 支付宝扫描解析规则
        /// </summary>
        public class StrategyAlipay : IStrategy
        {
            /// <summary>
            /// 扫描策略
            /// </summary>
            /// <param name="text"></param>
            public void AnalysisAction(string text)
            {
                //使用->分割,得到解析对象。
            }
        }
    支付宝扫描解析策略
        /// <summary>
        /// 接口管理类
        /// </summary>
        public class StrategyContext
        {
            private IStrategy strategy;
            /// <summary>
            /// 外层调用的时候决定使用哪个扫描策略
            /// </summary>
            /// <param name="strategy"></param>
            public StrategyContext(IStrategy strategy)
            {
                this.strategy = strategy;
            }
            public void AnalysisAction(string text)
            {
                strategy.AnalysisAction(text);
            }
        }
    管理类

    这样我们的业务逻辑就可以这样写:

            private StrategyContext Context;
            /// <summary>
            /// 解析方法
            /// </summary>
            /// <param name="text">扫描得到的文本</param>
            public void AnalysisAction(string text)
            {
    
                //微信解析方法
                if (text.Contains("WeChat"))
                {
                    Context = new StrategyContext(new StrategyWeChat());
                }
                //支付宝解析方法
                else if (text.Contains("Alipay"))
                {
                    Context = new StrategyContext(new StrategyAlipay());
                }
                Context.AnalysisAction(text);
            }

      我们将具体的解析规则放到了具体的实现类中,但是我们并没有消灭If else,如果在添加联通的扫码解析的话,还是需要修改代码,添加else。

      这就是策略模式的缺点,必须知道要使用的具体的策略,也就是有的人说的还是要使用if else。

    升级改造

      因为前面说到了策略模式的缺点。如果就是要消灭if else呢?我们可以将决定使用策略的决定权放到具体策略实现类中。

     1     /// <summary>
     2     /// 扫描解析规则抽象接口
     3     /// </summary>
     4     public interface IStrategy
     5     {
     6         /// <summary>
     7         /// 是否可以解析
     8         /// </summary>
     9         bool Analysisable { get; }
    10 
    11         /// <summary>
    12         /// 扫描解析方法策略
    13         /// </summary>
    14         /// <param name="text"></param>
    15         void AnalysisAction();
    16     }
    扫描解析规则抽象接口添加是否可以解析属性
        /// <summary>
        /// 微信扫描解析规则
        /// </summary>
        public class StrategyWeChat : IStrategy
        {
            private string _text;
            public StrategyWeChat(string text)
            {
                this._text = text;
            }
            public bool Analysisable
            {
                get { return _text.Contains("WeChat"); }
            }
    
            /// <summary>
            /// 扫描策略
            /// </summary>
            /// <param name="text"></param>
            public void AnalysisAction()
            {
                //拿着_text到微信服务器解析,返回解析对象。
            }
        }
    微信
        /// <summary>
        /// 支付宝扫描解析规则
        /// </summary>
        public class StrategyAlipay : IStrategy
        {
            private string _text;
            public StrategyAlipay(string text)
            {
                this._text = text;
            }
            public bool Analysisable
            {
                get { return _text.Contains("Alipay"); }
            }
            /// <summary>
            /// 扫描策略
            /// </summary>
            /// <param name="text"></param>
            public void AnalysisAction()
            {
                //使用->分割,得到解析对象。
            }
        }
    支付宝
     1     public class StrategyContext2
     2     {
     3         private readonly IList<IStrategy> strategyList = new List<IStrategy>();
     4         /// <summary>
     5         /// 将所有策略都方法
     6         /// </summary>
     7         /// <param name="text"></param>
     8         public StrategyContext2(string text)
     9         {
    10             strategyList.Add(new StrategyWeChat(text));
    11             strategyList.Add(new StrategyAlipay(text));
    12         }
    13         /// <summary>
    14         /// 调用具体的策略类实现扫码解析方法
    15         /// </summary>
    16         public void AnalysisAction()
    17         {
    18             foreach (var item in strategyList)
    19             {
    20                 if (item.Analysisable)//判断当前策略类是否可以处理
    21                     item.AnalysisAction();
    22             }
    23         }
    24     }
    Context
    1         private StrategyContext2 Context;
    2         public void AnalysisAction(string text)
    3         {
    4             Context = new StrategyContext2(text);
    5             Context.AnalysisAction();//自动实现解析,不用关心使用哪种策略
    6         }

    这样我们就想决定权放到了具体策略类本身中。消灭了If else。如果再添加联通扫码策略的时候,只需要添加联通的具体扫描策略,然后在context构造函数中把他加入到策略集合中。

    但是这样我们还是修改了context代码。如果继续想不修改context代码呢?

    继续升级

    我们可以使用反射,将所有策略实现类都反射出来,添加到策略集合中。那么我们的context类可以这样写:

        public class StrategyContext3
        {
            private readonly IList<IStrategy> strategyList = new List<IStrategy>();
            /// <summary>
            /// 将所有策略都方法
            /// </summary>
            /// <param name="text"></param>
            public StrategyContext3(string text)
            {
                //查询程序集
                Assembly assembly = Assembly.GetExecutingAssembly();
                //找出继承扫描策略接口的类
                IEnumerable<Type> types = assembly.GetTypes().Where(c => c.GetInterface("IStrategy") != null);
                foreach (var t in types)
                {
                    object[] parameters = new object[1];
                    parameters[0] = text;
                    //创建类的实例
                    strategyList.Add((IStrategy)Activator.CreateInstance(t, parameters));
                }
            }
            /// <summary>
            /// 调用具体的策略类实现扫码解析方法
            /// </summary>
            public void AnalysisAction()
            {
                foreach (var item in strategyList)
                {
                    if (item.Analysisable)//判断当前策略类是否可以处理
                        item.AnalysisAction();
                }
            }
        }
    context

    应用场景

    1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
    2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
    3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

    总结

      优点:策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。

      缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。 这也就是我们所说的if else并没有真正的被消灭。

      改进:我们可以将决定使用哪种策略的权利放到策略类本身中,让策略自己决定到底是不是用我自己的方法。从而实现消灭if else。

         可以通过反射,反射出所有的策略。这样比较符合开闭原则。

  • 相关阅读:
    delphi Int64Rec 应用实例
    PerformEraseBackground 擦除背景(ThemeServices)
    Delphi 的 Bit
    delphi 关于 "高位" 与 "低位"
    PByte和PChar的关系
    执行控制台程序并且获得它的输出结果
    Console下Pause效果的函数
    ByteType字符串中判断是否英文
    窗体包括标题作为一个位图复制到剪贴板
    inf 启动
  • 原文地址:https://www.cnblogs.com/alua/p/7267698.html
Copyright © 2020-2023  润新知