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

时间:2021-05-06 00:03:47

概述

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

背景

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

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

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

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

  

         /// <summary>
/// 解析方法
/// </summary>
/// <param name="text">扫描得到的文本</param>
public void AnalysisAction(string text)
{
//微信解析方法
if (text.Contains("WeChat"))
{
//拿着text到微信服务器解析,返回解析对象。
}
//支付宝解析方法
else if (text.Contains("Alipay"))
{
//使用->分割,得到解析对象。
}
}

问题

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

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

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

改进(抽象)

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

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

  策略模式的uml图如下:设计模式-策略模式Strategy以及消灭if else

  

 
    /// <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呢?我们可以将决定使用策略的决定权放到具体策略实现类中。

     /// <summary>
/// 扫描解析规则抽象接口
/// </summary>
public interface IStrategy
{
/// <summary>
/// 是否可以解析
/// </summary>
bool Analysisable { get; } /// <summary>
/// 扫描解析方法策略
/// </summary>
/// <param name="text"></param>
void AnalysisAction();
}

扫描解析规则抽象接口添加是否可以解析属性

    /// <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()
{
//使用->分割,得到解析对象。
}
}

支付宝

     public class StrategyContext2
{
private readonly IList<IStrategy> strategyList = new List<IStrategy>();
/// <summary>
/// 将所有策略都方法
/// </summary>
/// <param name="text"></param>
public StrategyContext2(string text)
{
strategyList.Add(new StrategyWeChat(text));
strategyList.Add(new StrategyAlipay(text));
}
/// <summary>
/// 调用具体的策略类实现扫码解析方法
/// </summary>
public void AnalysisAction()
{
foreach (var item in strategyList)
{
if (item.Analysisable)//判断当前策略类是否可以处理
item.AnalysisAction();
}
}
}

Context

         private StrategyContext2 Context;
public void AnalysisAction(string text)
{
Context = new StrategyContext2(text);
Context.AnalysisAction();//自动实现解析,不用关心使用哪种策略
}

这样我们就想决定权放到了具体策略类本身中。消灭了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[];
parameters[] = 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。

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

设计模式-策略模式Strategy以及消灭if else的更多相关文章

  1. &lbrack;&period;net 面向对象程序设计深入&rsqb;(26)实战设计模式——策略模式 Strategy (行为型)

    [.net 面向对象程序设计深入](26)实战设计模式——策略模式 Strategy (行为型) 1,策略模式定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模 ...

  2. 设计模式--策略模式&lpar;strategy&rpar;

    1.策略模式(strategy ['strætədʒi]) 我的理解是:方案候选模式 (反正关键就是有很多的候选,哈哈) 看了很多例子,都是在说鸭子的,那个例子很好,在这里可以看 他们生产鸭子,我们就 ...

  3. 设计模式 - 策略模式&lpar;Strategy Pattern&rpar; 具体解释

    策略模式(Strategy Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26577879 本文版权全 ...

  4. java设计模式 策略模式Strategy

    本章讲述java设计模式中,策略模式相关的知识点. 1.策略模式定义 策略模式,又叫算法簇模式,就是定义了不同的算法族,并且之间可以互相替换,此模式让算法的变化独立于使用算法的客户.策略模式属于对象的 ...

  5. 设计模式——策略模式&lpar;Strategy Pattern&rpar;

    写在前面: 直接将书中的例子用来作为记录自己学习的成果,不知道这样好不好,如果给原作者带来什么不利的影响不妨告知一声,我及时删掉. UML图: 抽象策略:Strategy package com.cn ...

  6. 说说设计模式~策略模式&lpar;Strategy&rpar;

    返回目录 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.而对于客户端(UI)来说,可以通过IOC再配合工厂模块,实现动态策略的切换,策略模块通常于一个抽象策略对象(in ...

  7. C&num;设计模式——策略模式&lpar;Strategy Pattern&rpar;

    一.概述我们来实现一个企业的工资系统,该企业中不同级别的员工工资算法都不相同,针对该问题,最容易想到的莫过于在代码中堆积一大堆if…else…语句或者是switch…case…语句.如果该企业中不同级 ...

  8. 设计模式---策略模式Strategy(对象行为型)

    1. 概述 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不 ...

  9. 大话设计模式--策略模式 strategy -- C&plus;&plus;实现实例

    1. 策略模式: 它定义了算法家族, 分别封装起来,使他们之间可以相互替换,此模式让算法变化, 不会影响到使用算法的客户. 用相同的方法调用不同的算法,减少各种算法类与使用算法类之间的耦合. 实例中策 ...

随机推荐

  1. 【原】React操作表单

    最近的项目中开发中都是用react,其中有用到react去操纵表单.然后自己就在每个表单元素中添加 ref,  然后再像jquery操作dom一样去操纵这个ref, 代码如下: 首先我在每个表单元素那 ...

  2. 适用于Magento的最合适的&period;htaccess写法

    原作者地址:http://www.ctrol.cn/post/ecommercial/magento/12-05-ctrol-4057.html ########################### ...

  3. BZOJ 3159决战

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3159 题意:给出一棵树,(1)路径加一个值:(2)路径上的节点的值反转(只是值反转,不是节 ...

  4. processon完全装逼指南

    一.引言 作为一名IT从业者,不仅要有扎实的知识储备,出色的业务能力,还需要具备一定的软实力.软实力体现在具体事务的处理能力,包括沟通,协作,团队领导,问题的解决方案等,这些能力在关键时刻比硬性的技术 ...

  5. MySQL查询指定时间的数据

    user_event :用户事件表 create_time :表中存储时间的字段 #获取当月数据 SELECT * FROM user_event WHERE DATE_FORMAT(create_t ...

  6. redis编译问题

    在编译redis时,出现以下问题 In file included from adlist.c:34:0: zmalloc.h:50:31: fatal error: jemalloc/jemallo ...

  7. HDU 3389 阶梯博弈变形

    n堆石子,每次选取两堆a!=b,(a+b)%2=1 && a!=b && 3|a+b,不能操作者输 选石子堆为奇数的等价于选取步数为奇数的,观察发现 1 3 4 是无法 ...

  8. 6&period;3 cmath--数学函数

    本模块提供了处理复数的数学函数.因此这些函数接受整数.浮点数或者复数作为參数. 6.3.1 与极坐标相互转换的函数 在Python里表示一个复数z,实部使用z.real表示,虚部使用z.imag,能够 ...

  9. &lbrack;BZOJ4887&rsqb;&lbrack;TJOI2017&rsqb;可乐&lpar;DP&plus;矩阵快速幂&rpar;

    题目描述 加里敦星球的人们特别喜欢喝可乐.因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的1号城市上.这个可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆.它每一秒都会随机 ...

  10. JPA ID生成策略&lpar;转---&rpar;

    尊重原创:http://tendyming.iteye.com/blog/2024985 JPA ID生成策略 @Table Table用来定义entity主表的name,catalog,schema ...