我这里的业务场景是根据消息类型将离线消息存入mongoDB不同的collection中。其中就涉及到大量的分支判断,为了增强代码的可读性和可维护性,对之前的代码进行了重构。

先对比一下使用反射+策略模式前后的代码:

重构前:

重构后:

我们可以看到重构前的代码充斥着大量的分支判断,以后每增加一个新的消息类型就要增加一个新的具体实现类和增加一个新的分支判断,可拓展性是相当差的;而重构后的代码当需要增加一个新的消息类型时,只需要增加一个具体的实现类就可以实现,根本不需要考虑分支判断,这也是我们希望看到的。

接下来我们看一下具体的实现过程:

1.抽象策略类

定义了具体策略类需要执行的具体操作,并且对外部提供了一个触发操作的方法。抽象策略类中还定义了两个属性LogSource和Operation,这样mongoDB中的Collection名和消息枚举类型Operation就形成一对一的关系。注意我们把这两个属性的set权限定义为protected ,属性赋值操作在具体策略类实现。实现了不同的消息类型对应不同的具体策略类对应不同的mongo Collection。

 public abstract class SaveOffLineMessageTemplate<T>
{
/// <summary>
/// 日志源和mongo表名
/// </summary>
public string LogSource { get; protected set; }
/// <summary>
/// 操作类型
/// </summary>
public Operation Operation { get; protected set; } /// <summary>
/// 保存消息到mongoDB
/// </summary>
/// <param name="message">消息</param>
/// <param name="userRole">接收用户角色</param>
/// <param name="messageSendTag">是否发送标志 0未发送 1已发送</param>
/// <returns></returns>
public async Task<bool> AddMessageToMongo(string message, UserRoleEnum userRole, int messageSendTag)
{
//消息反序列化
var model = DeserializeObject(message);
//获取用户设备集合
var devices = QueryUserDevice(model);
//组装数据
var combineData = CombineData(model, devices, userRole);
//记录日志
Log.MyLog.Info(LogSource + "AddMessageToMongo", "保存消息到mongoDB", JsonConvert.SerializeObject(model) + JsonConvert.SerializeObject(devices));
//保存到mongoDB
return await MessageDB.AddOffLineMessage(combineData, LogSource);
}
/// <summary>
/// 消息反序列化
/// </summary>
/// <typeparam name="T">模板类</typeparam>
/// <param name="message">消息</param>
/// <returns></returns>
public abstract T DeserializeObject(string message); /// <summary>
/// 获取用户设备集合
/// </summary>
/// <returns></returns>
public abstract List<DevicesMongoModel> QueryUserDevice(T model); /// <summary>
/// 组装数据
/// </summary>
/// <returns></returns>
public abstract MessageMongoModel CombineData(T model, List<DevicesMongoModel> devices, UserRoleEnum userRole, int messageSendTag);
}

2.具体策略类

实现了抽象策略类定义的操作。在此处给抽象策略类的消息操作类型Operation和LogSource进行赋值。

  public class SaveLoginPasswordModify : SaveOffLineMessageTemplate<FinanceBase>
{
public SaveLoginPasswordModify() : base()
{
this.Operation = Operation.登陆密码变更消息推送;
this.LogSource = "Retail";
} public override FinanceBase DeserializeObject(string message)
{
FinanceBase model = JsonConvert.DeserializeObject<FinanceBase>(message);
return model;
} public override List<DevicesMongoModel> QueryUserDevice(FinanceBase model)
{
//设备编码集合
List<DevicesMongoModel> devicesList = null;
//用户信息
ClientsMongoModel mongoModel = MongoOper.QueryUserDevices(model.UserId); if(mongoModel!=null && mongoModel.DeviceIdList!=null && mongoModel.DeviceIdList.Count>)
{
//组装设备编码集合
devicesList = (from d in mongoModel.DeviceIdList
select new DevicesMongoModel
{
Device = d,
MessageSendTag =
}).ToList();
}
return devicesList;
} public override MessageMongoModel CombineData(FinanceBase model, List<DevicesMongoModel> devices, UserRoleEnum userRole, int messageSendTag)
{
MessageMongoModel mongoModel = new MessageMongoModel()
{
MessageId = model.MessageId,
MessageTitle = model.MsgTitle,
MessageContent = model.MsgContent,
MessageExtras = model.MessageExtras,
MessageType = model.MessageType,
UserId = model.UserId,
UserRole = userRole,
Devices = devices //设备集合
};
return mongoModel;
}
}

3.环境类

定义了一个字典用于存放消息类型Operation和抽象策略类SaveOffLineMessageTemplate<T>的对应关系,字典Value中实际上存放的是具体的策略类(里氏替换原则)。这里就是把switch case中每个消息类型Operation及其对应的分支操作抽象为字典中一对一的关系。

然后利用反射动态的创建具体策略类实例并将其加入字典,每次请求过来时,都会匹配字典中是否存在以此次请求的消息类型Operation为key的项,如果存在就会执行抽象策略类中的AddMessageToMongo方法,实际上就是执行了具体策略类中的操作方法。这样就间接实

现了switch case根据请求带来的参数分发到不同的处理类。

此处应该注意的是反射的效率是比较低的,所以环境类SaveOffMessageToMongo<T>的构造函数应该设为static静态的,保证只有第一次请求时才会执行反射创建对象,而之后的所有请求都不再创建对象。而switch case实现方式中每次请求都会创建对象。这也是使用反射+策略模式的一个优点,避免了创建实例过程中的资源和时间的消耗。

 public class SaveOffMessageToMongo<T>
{
public static Dictionary<Operation, SaveOffLineMessageTemplate<T>> dicSaveOffMessage; #region 利用反射+策略模式解决operation大量的switch case
/// <summary>
/// 利用反射+策略模式解决operation大量的switch case
/// </summary>
static SaveOffMessageToMongo()
{
//1.创建一个字典用于存放 消息类型-具体策略
dicSaveOffMessage = new Dictionary<Operation, SaveOffLineMessageTemplate<T>>();
//2.获取类型的 System.Type 对象
Type abjType = typeof(SaveOffLineMessageTemplate<T>);
//3.获取此类型所在的程序集
Assembly assem = abjType.Assembly;
//4.遍历获取此程序集中所有的类
foreach (Type t in assem.GetTypes())
{
//5.是类并且不是抽象类并且继承自抽象策略类(只有具体策略类符合)
if (t.IsClass && !t.IsAbstract && t.IsSubclassOf(abjType))
{
//6.如果符合就创建一个具体策略类的实例,并装换为抽象策略类类型
SaveOffLineMessageTemplate<T> template = Activator.CreateInstance(t) as SaveOffLineMessageTemplate<T>;
//7.如果字典中不存在以实例的消息类型Operation为key的项,就添加至字典
if (template != null && !dicSaveOffMessage.ContainsKey(template.operation))
{
dicSaveOffMessage.Add(template.operation, template);
}
}
}
}
#endregion #region 添加消息到MongoDB
/// <summary>
/// 添加消息到MongoDB
/// </summary>
/// <param name="message">消息</param>
/// <param name="operation">操作类型</param>
/// <param name="userRole">用户类型</param>
/// <param name="messageSendTag">消息发送标志</param>
public static void AddMessageToMongo(string message, Operation operation, UserRoleEnum userRole, int messageSendTag = )
{
//8.如果字典中存在以operation为key的项,就调用对应的抽象策略类中的AddMessageToMongo方法
if (dicSaveOffMessage.ContainsKey(operation))
{
dicSaveOffMessage[operation].AddMessageToMongo(message, userRole, messageSendTag);
}
}
#endregion
}

总结:

使用反射+策略模式代替项目中大量的switch case判断的优点:

1.代码的扩展性好,当有新的消息类型需要处理时,只需要添加一个具体策略类进行处理即可,完全不必关心环境类的实现。

2.避免了创建实例过程中的资源和时间的消耗。

缺点:

1.反射的效率较低,如果抽象策略类所在的程序集拥有的类较多时,反射效率较低的缺点就会比较明显。因为需要进行大量的循环遍历才能找到符合条件的具体策略类。

使用反射+策略模式代替项目中大量的switch case判断的更多相关文章

  1. 【设计模式】【应用】使用模板方法设计模式、策略模式 处理DAO中的增删改查

    原文:使用模板方法设计模式.策略模式 处理DAO中的增删改查 关于模板模式和策略模式参考前面的文章. 分析 在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大. ...

  2. 【Spring源码解析】—— 策略模式在Spring中的应用

    一.         什么是策略模式 策略模式的定义/含义:策略本身就是为了实现某一个目标而采取的一种工作方式,因此只要能够达成目标,则采取哪一种策略都可以:因此多种实际的策略之间是相互平行的. 注意 ...

  3. 策略模式在ThreadpoolExecutor中的应用

    偶然读到ThreadpoolExecutor的源码,发现里面使用到了策略模式,抓出来和大家分享下: 感兴趣的朋友们可以读读 ThreadPoolExecutor的源码: public void set ...

  4. 企业应用开发模式 ERP项目中应用到的技术和工具

    一.基础技术选型 C# .NET 3.5/4.0  这两个版本的.NET已经相当方便(Linq, Lambda,Parallel),语法简洁,配合WCF和WF两项技术,可以满足快速开发,维护方便的目标 ...

  5. SD卡两种操作模式在项目中应用的比较

    1.SDIO接口传输速度比SPI接口传输速度快:2.STM32的SDIO口还真的不好用 特别是4BIT的方式 我都纠结了好久了 用1BIT的方式倒是可以 速度大概可以到读2M字节每秒(STM32F20 ...

  6. js中if else switch 条件判断的替代方法

    function condition(test){ return({ cat :function(){console.log('cat');}, dog :function(){console.log ...

  7. ArcEngine几何变换中的策略模式

    使用策略模式可以减少分支语句,switch...Case,同时便于策略的扩展. 1. ITransform2D接口的Transform方法: [C#]public void Transform ( e ...

  8. MVP模式在Android项目中的使用

    以前在写项目的时候,没有过多考虑架构模式的问题,因为之前一直做J2EE开发,而J2EE都是采用MVC模式进行开发的,所以在搭建公司项目的时候,也是使用类似MVC的架构(严格来讲,之前的项目还算不上MV ...

  9. 在商城系统中使用设计模式----策略模式之在spring中使用策略模式

    1.前言: 这是策略模式在spring中的使用,对策略模式不了解对同学可以移步在商城中简单对使用策略模式. 2.问题: 在策略模式中,我们创建表示各种策略的对象和一个行为,随着策略对象改变而改变的 c ...

随机推荐

  1. Hexo博客添加SEO-评论系统-阅读统计-站长统计

    原文地址:→传送门 写在前面 在五月出捣腾了一把个人博客,但是刚开始只做了一些基础设置,套路也没摸清,基础安装篇请看hexo从零开始到搭建完整,里面讲到了基础工具的安装及blog项目的文件夹含义,以及 ...

  2. css简单实现五角星评分、点赞收藏、展示评分(半颗星、1/3颗星)

    1.前言 之前做的好几个项目中,都会遇到打分,评分,点赞这样的需求,写了很多次,每次需要再写的时候,就会翻出之前写过的代码,然后copy过来.总觉得这样的话没有进步,没有把知识放进脑袋里,所以,自己花 ...

  3. JAVA 中BIO,NIO,AIO的理解以及 同步 异步 阻塞 非阻塞

    在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步 ...

  4. 灵玖软件NLPIRParser智能文本聚类

    随着互联网的迅猛发展,信息的爆炸式增加,信息超载问题变的越来越严重,信息的更新率也越来越高,用户在信息海洋里查找信息就像大海捞针一样.搜索引擎服务应运而生,在一定程度上满足了用户查找信息的需要.然而互 ...

  5. JavaScript中的call()、apply()与bind():

    关于call()与apply(): 在JavaScript中,每个函数都有call与apply(),这两个函数都是用来改变函数体内this的指向,并调用相关的参数. 看一个例子: 定义一个animal ...

  6. git分支管理之分支管理策略

    分支管理策略 阅读: 246888 通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息. 如果要强制禁用Fast forward模式,Git就 ...

  7. u盘安装原版win10系统1703更新

    7月底,微软发布了win10的1703更新,听说值得一试,于是,安装ultraISO(下载地址http://pan.baidu.com/s/1dFu48d7 密码: 53cg),下载win10的ISO ...

  8. Tp3.2提交表单与操作表单

    笔记笔记 先去建个表———— 然后把我输入的东西 存到表里: <input type="submit" id="tijiao" value="提 ...

  9. PAT1074 Reversing Linked List (25)详细题解

    02-1. Reversing Linked List (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue ...

  10. Quartus16.0如何使用TCL脚本

    前言 TCL脚本语言在EDA工具中使用频繁,本文主要介绍使用TCL脚本文件进行引脚分配,避免手动分配以及分配出错: 流程 1.准备好你的TCL脚本文件,举个栗子(脚本文件内容): 2.在Quartus ...