使用反射+策略模式代替项目中大量的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 ...
随机推荐
- EF 中 Code First 的数据迁移以及创建视图
写在前面: EF 中 Code First 的数据迁移网上有很多资料,我这份并没什么特别.Code First 创建视图网上也有很多资料,但好像很麻烦,而且亲测好像是无效的方法(可能是我太笨,没搞成功 ...
- 数据结构随笔-php实现栈
栈(Stack)满足后进先出(LIFO)的原则: 下面利用php实现栈的相关操作: 本实例栈的基本操作: 入栈(push):向栈内压入一个元素,栈顶指针指向栈顶元素 出栈(pop): 从栈顶去除元素, ...
- mybatis 详解(六)------通过mapper接口加载映射文件
通过 mapper 接口加载映射文件,这对于后面 ssm三大框架 的整合是非常重要的.那么什么是通过 mapper 接口加载映射文件呢? 我们首先看以前的做法,在全局配置文件 mybatis-conf ...
- js数组去重方法分析与总结
数组去重经常被人拿来说事,虽然在工作中不常用,但他能够很好的考察js基础知识掌握的深度和广度,下面从js的不同阶段总结一下去重的方法. ES3阶段 该阶段主要通过循环遍历数组从而达到去重的目的 多次循 ...
- 【机器学习PAI实践二】人口普查统计
一.背景 感谢大家关注玩转数据系列文章,我们希望通过在阿里云机器学习平台上提供demo数据并搭建相关的实验流程的方式来帮助大家学习如何通过算法来挖掘数据中的价值.本系列文章包含详细的实验流程以及相关的 ...
- 微信小程序添加、删除class’
终于等到公司开发小程序了,学习的时候不觉得有什么,实际开发就会出现各种问题. 今天第一天开发就遇到问题了. 项目需求,要一个平时的nav导航栏,这玩意用jQuery两行代码解决了,可是小程序不允许操作 ...
- Linux操作系统-命令-aptitude install unzip
如果linux系统没有自带unzip,请执行aptitude install unzip以安装. 使用到这条Linux命令的场景是: 当我把Jmeter的压缩包(xxx.zip)拷贝到远程的Linux ...
- Jedis与Redisson选型对比
1 概述 1.1. 主要内容 本文的主要内容为对比Redis的两个框架:Jedis与Redisson,分析各自的优势与缺点,为无线云管理项目中Redis编程模型的选择提供参考. 2. ...
- Android App 压力测试 monkeyrunner
Android App 压力测试 第一部分 背景 1. 为什么要开展压力测试? 2. 什么时候开展压力测试?第二部分 理论 1. 手工测试场景 2. 自动测试创建 3. Monkey工具 4. ADB ...
- PHP魔术方法
魔术方法:两个下划线开头的格式. PHP中的魔术方法总结 :__construct, __destruct , __call, __callStatic,__get, __set, __isset, ...