使用反射+策略模式代替项目中大量的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 ...
随机推荐
- WebService中的WSDL详解 及jmeter测试
首先简单讲解一下什么是wsdl. 一个WSDL文档通常包含8个重要的元素,即definitions.types.import.message.portType.operation.bin ...
- LigerUI LigerGrid getSelectedRows() 多选顺序 不是从上到下修改方法
1.问题 LigreGrid内部是选中一个,往selected里塞一个, 当执行getSelectedRows() 的时候,会把selected以选中的顺序,返回出来,所以是按照选择顺序返回. 原生代 ...
- 基于angular2x+ng-bootstrap构建后台管理系统界面(干货)
写在前面的话 近来公司要做一个后台管理系统,人手比较少,于是作为一个前端也参与进来,其实据我所知,大部分的公司还是后台自己捣鼓的. 在后台没有到位的情况下,前端应该使用什么技术也着实让我为难了一把.经 ...
- Head First 设计模式 第3章 装饰者模式
第3章 装饰者模式 1.定义/说明 动态.透明的将职责附加到对象上(或从对象上撤销),而不影响其他对象.若要扩展功能,装饰者模式提供了比继承更富有弹性的替代方案. 2.介绍 首先让我们先来介绍一下场景 ...
- mysql 触发器(trigger)
触发器(trigger):监视某种情况,并触发某种操作. 触发器创建语法四要素:1.监视地点(table) 2.监视事件(insert/update/delete) 3.触发时间(after/befo ...
- 27. Remove Element【leetcode】
27. Remove Element[leetcode] Given an array and a value, remove all instances of that value in place ...
- Jmeter==HTTP信息头管理器的作用
HTTP信息头管理器在Jmeter的使用过程中起着很重要的作用,通常我们在通过Jmeter向服务器发送http请求(get或者post)的时候,往往后端需要一些验证信息,比如说web服务器需要带过去c ...
- 《Python学习手册》读书笔记【转载】
转载:http://www.cnblogs.com/wuyuegb2312/archive/2013/02/26/2910908.html 之前为了编写一个svm分词的程序而简单学了下Python,觉 ...
- 【LeetCode】数组-6(561)-Array Partition I(比较抽象的题目)
题目描述:两句话发人深思啊.... Given an array of 2n integers, your task is to group these integers into n pairs o ...
- css实现未知高度水平垂直居中
页面设计中,经常需要实现元素的水平垂直居中,css实现的方法有很多(列如: margin: auto.position定位.css表达式calc().使用css预处理.table等都可以实现水平居中) ...