我这里的业务场景是根据消息类型将离线消息存入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. java Socket(详解)转载

    在客户/服务器通信模式中, 客户端需要主动创建与服务器连接的 Socket(套接字), 服务器端收到了客户端的连接请求, 也会创建与客户连接的 Socket. Socket可看做是通信连接两端的收发器 ...

  2. pwnable.kr random 之 write up

    ---恢复内容开始--- 首先看源代码: #include <stdio.h> int main(){ unsigned int random; random = rand(); // r ...

  3. 接口工具-POSTMAN

    前端的一项总要工作就是测试接口,当然这也可能是你们后台人员做的.不管怎样,都需要测试接口,那么就来介绍一款谷歌浏览器接口测试插件,postman.首先你要去谷歌的应用商店,搜索这个插件,(需要FQ), ...

  4. 蓝桥杯比赛关于 BFS 算法总结方法以及套路分析

    首先我们来看几道java A组的题目,都是同一年的哦!!! 搭积木 小明最近喜欢搭数字积木,一共有10块积木,每个积木上有一个数字,0~9. 搭积木规则:每个积木放到其它两个积木的上面,并且一定比下面 ...

  5. iOS源码博文集锦2

    iOS精选源码 快速集成观看直播和开播   一款类携程商旅的城市选择界面   一个类似于QQ电话的动画效果   高德地图定位,导航,轨迹,GPS纠偏   真实逻辑滚动数字DPScrollNumberL ...

  6. Python使用openpyxl读写excel文件

    Python使用openpyxl读写excel文件 这是一个第三方库,可以处理xlsx格式的Excel文件.pip install openpyxl安装.如果使用Aanconda,应该自带了. 读取E ...

  7. Linux Shell 1 - Print from terminal

    Two ways to print info from terminal - echo & printf - Echo a. Exclamation mark is supported in ...

  8. git bash命令行使用https协议方式进行克隆和提交到github服务器

    在本地创建一个文件夹来存放远程服务器仓库:如创建一个git8文件夹: 在命令行中,使用git clone https://github.com/serverking/weixin.git进行克隆git ...

  9. PyCharm中光标变粗的解决方法

    pycharm中光标变粗,如下: 原因:光标进入了改写状态. 解决方法:按一下键盘中的Insert键就好了.

  10. 使用 Node.js 搭建一个 API 网关

    原文地址:Building an API Gateway using Node.js 外部客户端访问微服务架构中的服务时,服务端会对认证和传输有一些常见的要求.API 网关提供共享层来处理服务协议之间 ...