使用反射+策略模式代替项目中大量的switch case判断
我这里的业务场景是根据消息类型将离线消息存入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判断的更多相关文章
- 【设计模式】【应用】使用模板方法设计模式、策略模式 处理DAO中的增删改查
原文:使用模板方法设计模式.策略模式 处理DAO中的增删改查 关于模板模式和策略模式参考前面的文章. 分析 在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大. ...
- 【Spring源码解析】—— 策略模式在Spring中的应用
一. 什么是策略模式 策略模式的定义/含义:策略本身就是为了实现某一个目标而采取的一种工作方式,因此只要能够达成目标,则采取哪一种策略都可以:因此多种实际的策略之间是相互平行的. 注意 ...
- 策略模式在ThreadpoolExecutor中的应用
偶然读到ThreadpoolExecutor的源码,发现里面使用到了策略模式,抓出来和大家分享下: 感兴趣的朋友们可以读读 ThreadPoolExecutor的源码: public void set ...
- 企业应用开发模式 ERP项目中应用到的技术和工具
一.基础技术选型 C# .NET 3.5/4.0 这两个版本的.NET已经相当方便(Linq, Lambda,Parallel),语法简洁,配合WCF和WF两项技术,可以满足快速开发,维护方便的目标 ...
- SD卡两种操作模式在项目中应用的比较
1.SDIO接口传输速度比SPI接口传输速度快:2.STM32的SDIO口还真的不好用 特别是4BIT的方式 我都纠结了好久了 用1BIT的方式倒是可以 速度大概可以到读2M字节每秒(STM32F20 ...
- js中if else switch 条件判断的替代方法
function condition(test){ return({ cat :function(){console.log('cat');}, dog :function(){console.log ...
- ArcEngine几何变换中的策略模式
使用策略模式可以减少分支语句,switch...Case,同时便于策略的扩展. 1. ITransform2D接口的Transform方法: [C#]public void Transform ( e ...
- MVP模式在Android项目中的使用
以前在写项目的时候,没有过多考虑架构模式的问题,因为之前一直做J2EE开发,而J2EE都是采用MVC模式进行开发的,所以在搭建公司项目的时候,也是使用类似MVC的架构(严格来讲,之前的项目还算不上MV ...
- 在商城系统中使用设计模式----策略模式之在spring中使用策略模式
1.前言: 这是策略模式在spring中的使用,对策略模式不了解对同学可以移步在商城中简单对使用策略模式. 2.问题: 在策略模式中,我们创建表示各种策略的对象和一个行为,随着策略对象改变而改变的 c ...
随机推荐
- MySql 求一段时间范围内的每一天,每一小时,每一分钟
平常经常会求一段时间内的每一天统计数据,或者每一时点的统计数据.但是mysql本身是没有直接获取时点列表的函数或表.下面是自己用到的一些方法,利用临时变量和一个已存在的比较多数据(这个需要根据实际情况 ...
- [Tyvj模拟赛]运
运 题目 [问题背景]zhx 和妹子们玩数数游戏. [问题描述] 仅包含4或7的数被称为幸运数.一个序列的子序列被定义为从序列中删去若干个数, 剩下的数组成的新序列.两个子序列被定义为不同的当且仅当其 ...
- zabbix前台jsrpc注入
zabbix是一个开源的企业级性能监控解决方案. 官方网站:http://www.zabbix.com zabbix的jsrpc的profileIdx2参数存在insert方式的SQL注入漏洞,攻击者 ...
- mongodb分片部署
Mongodb 分片部署 配置mongodb集群,比如 在3个server上配置 3 shard的Mongodb集群: 架构: 1.每片数据需要3个mongod server,2个为主从数据节点:1个 ...
- vc操作电脑之常用命令
1.重启计算机: ExitWindowsEx(EWX_REBOOT,0); 2.关机: ExitWindowsEx(EWX_SHUTDOWN,0); 3.注销: ExitWindowsEx(EWX_L ...
- macOS下加载动态库dylib报"code signature invalid"错误的解决办法
一.现象描述 在macOS上搞开发也有一段时间了,也积攒了一定的经验.然而,今天在替换工程中的一个动态库时还是碰到了一个问题.原来工程中用的是一个静态库,调试时发现有问题就把它替换成了动态库.这本来没 ...
- 解决Nuget:https://api.nuget.org/v3/index.json 访问不了的问题
最近在家中用使用VS编译项目时,Nuget包一直下载不了,直接在浏览器中访问https://api.nuget.org/v3/index.json ,浏览器也打不开网址.把https协议改成http协 ...
- How to get the mapping relationship between two columns in a table
If a table have column A and B Count(distinct A) as Da Count(distinct B) as Db Count(distinct A, B) ...
- 一些方便的bash命令
1.文件名大小写转换: (1)大写转小写: ls | awk '{printf("mv %s %s\n", $0, tolower($0))|"sh"}' (2 ...
- MySQL GROUP BY多个字段分组用法详解
mysql语句中group by 很容易理解 是分组查询.比如 select sum(score) from user group by name 意思是查询每个人的分数总和但是, select su ...