我这里的业务场景是根据消息类型将离线消息存入mongoDB不一样的collection中。其中就涉及到大量的分支判断,为了加强代码的可读性和可维护性,对以前的代码进行了重构。async
先对比一下使用反射+策略模式先后的代码:ide
咱们能够看到重构前的代码充斥着大量的分支判断,之后每增长一个新的消息类型就要增长一个新的具体实现类和增长一个新的分支判断,可拓展性是至关差的;而重构后的代码当须要增长一个新的消息类型时,只须要增长一个具体的实现类就能够实现,根本不须要考虑分支判断,这也是咱们但愿看到的。函数
定义了具体策略类须要执行的具体操做,而且对外部提供了一个触发操做的方法。抽象策略类中还定义了两个属性LogSource和Operation,这样mongoDB中的Collection名和消息枚举类型Operation就造成一对一的关系。注意咱们把这两个属性的set权限定义为protected ,属性赋值操做在具体策略类实现。实现了不一样的消息类型对应不一样的具体策略类对应不一样的mongo Collection。this
1 public abstract class SaveOffLineMessageTemplate<T> 2 { 3 /// <summary> 4 /// 日志源和mongo表名 5 /// </summary> 6 public string LogSource { get; protected set; } 7 /// <summary> 8 /// 操做类型 9 /// </summary> 10 public Operation Operation { get; protected set; } 11 12 /// <summary> 13 /// 保存消息到mongoDB 14 /// </summary> 15 /// <param name="message">消息</param> 16 /// <param name="userRole">接收用户角色</param> 17 /// <param name="messageSendTag">是否发送标志 0未发送 1已发送</param> 18 /// <returns></returns> 19 public async Task<bool> AddMessageToMongo(string message, UserRoleEnum userRole, int messageSendTag) 20 { 21 //消息反序列化 22 var model = DeserializeObject(message); 23 //获取用户设备集合 24 var devices = QueryUserDevice(model); 25 //组装数据 26 var combineData = CombineData(model, devices, userRole); 27 //记录日志 28 Log.MyLog.Info(LogSource + "AddMessageToMongo", "保存消息到mongoDB", JsonConvert.SerializeObject(model) + JsonConvert.SerializeObject(devices)); 29 //保存到mongoDB 30 return await MessageDB.AddOffLineMessage(combineData, LogSource); 31 } 32 /// <summary> 33 /// 消息反序列化 34 /// </summary> 35 /// <typeparam name="T">模板类</typeparam> 36 /// <param name="message">消息</param> 37 /// <returns></returns> 38 public abstract T DeserializeObject(string message); 39 40 /// <summary> 41 /// 获取用户设备集合 42 /// </summary> 43 /// <returns></returns> 44 public abstract List<DevicesMongoModel> QueryUserDevice(T model); 45 46 /// <summary> 47 /// 组装数据 48 /// </summary> 49 /// <returns></returns> 50 public abstract MessageMongoModel CombineData(T model, List<DevicesMongoModel> devices, UserRoleEnum userRole, int messageSendTag); 51 }
实现了抽象策略类定义的操做。在此处给抽象策略类的消息操做类型Operation和LogSource进行赋值。编码
1 public class SaveLoginPasswordModify : SaveOffLineMessageTemplate<FinanceBase> 2 { 3 public SaveLoginPasswordModify() : base() 4 { 5 this.Operation = Operation.登录密码变动消息推送; 6 this.LogSource = "Retail"; 7 } 8 9 public override FinanceBase DeserializeObject(string message) 10 { 11 FinanceBase model = JsonConvert.DeserializeObject<FinanceBase>(message); 12 return model; 13 } 14 15 public override List<DevicesMongoModel> QueryUserDevice(FinanceBase model) 16 { 17 //设备编码集合 18 List<DevicesMongoModel> devicesList = null; 19 //用户信息 20 ClientsMongoModel mongoModel = MongoOper.QueryUserDevices(model.UserId); 21 22 if(mongoModel!=null && mongoModel.DeviceIdList!=null && mongoModel.DeviceIdList.Count>0) 23 { 24 //组装设备编码集合 25 devicesList = (from d in mongoModel.DeviceIdList 26 select new DevicesMongoModel 27 { 28 Device = d, 29 MessageSendTag = 0 30 }).ToList(); 31 } 32 return devicesList; 33 } 34 35 public override MessageMongoModel CombineData(FinanceBase model, List<DevicesMongoModel> devices, UserRoleEnum userRole, int messageSendTag) 36 { 37 MessageMongoModel mongoModel = new MessageMongoModel() 38 { 39 MessageId = model.MessageId, 40 MessageTitle = model.MsgTitle, 41 MessageContent = model.MsgContent, 42 MessageExtras = model.MessageExtras, 43 MessageType = model.MessageType, 44 UserId = model.UserId, 45 UserRole = userRole, 46 Devices = devices //设备集合 47 }; 48 return mongoModel; 49 } 50 }
定义了一个字典用于存放消息类型Operation和抽象策略类SaveOffLineMessageTemplate<T>的对应关系,字典Value中实际上存放的是具体的策略类(里氏替换原则)。这里就是把switch case中每一个消息类型Operation及其对应的分支操做抽象为字典中一对一的关系。spa
而后利用反射动态的建立具体策略类实例并将其加入字典,每次请求过来时,都会匹配字典中是否存在以这次请求的消息类型Operation为key的项,若是存在就会执行抽象策略类中的AddMessageToMongo方法,实际上就是执行了具体策略类中的操做方法。这样就间接实日志
现了switch case根据请求带来的参数分发到不一样的处理类。code
此处应该注意的是反射的效率是比较低的,因此环境类SaveOffMessageToMongo<T>的构造函数应该设为static静态的,保证只有第一次请求时才会执行反射建立对象,而以后的全部请求都再也不建立对象。而switch case实现方式中每次请求都会建立对象。这也是使用反射+策略模式的一个优势,避免了建立实例过程当中的资源和时间的消耗。对象
1 public class SaveOffMessageToMongo<T> 2 { 3 public static Dictionary<Operation, SaveOffLineMessageTemplate<T>> dicSaveOffMessage; 4 5 #region 利用反射+策略模式解决operation大量的switch case 6 /// <summary> 7 /// 利用反射+策略模式解决operation大量的switch case 8 /// </summary> 9 static SaveOffMessageToMongo() 10 { 11 //1.建立一个字典用于存放 消息类型-具体策略 12 dicSaveOffMessage = new Dictionary<Operation, SaveOffLineMessageTemplate<T>>(); 13 //2.获取类型的 System.Type 对象 14 Type abjType = typeof(SaveOffLineMessageTemplate<T>); 15 //3.获取此类型所在的程序集 16 Assembly assem = abjType.Assembly; 17 //4.遍历获取此程序集中全部的类 18 foreach (Type t in assem.GetTypes()) 19 { 20 //5.是类而且不是抽象类而且继承自抽象策略类(只有具体策略类符合) 21 if (t.IsClass && !t.IsAbstract && t.IsSubclassOf(abjType)) 22 { 23 //6.若是符合就建立一个具体策略类的实例,并装换为抽象策略类类型 24 SaveOffLineMessageTemplate<T> template = Activator.CreateInstance(t) as SaveOffLineMessageTemplate<T>; 25 //7.若是字典中不存在以实例的消息类型Operation为key的项,就添加至字典 26 if (template != null && !dicSaveOffMessage.ContainsKey(template.operation)) 27 { 28 dicSaveOffMessage.Add(template.operation, template); 29 } 30 } 31 } 32 } 33 #endregion 34 35 #region 添加消息到MongoDB 36 /// <summary> 37 /// 添加消息到MongoDB 38 /// </summary> 39 /// <param name="message">消息</param> 40 /// <param name="operation">操做类型</param> 41 /// <param name="userRole">用户类型</param> 42 /// <param name="messageSendTag">消息发送标志</param> 43 public static void AddMessageToMongo(string message, Operation operation, UserRoleEnum userRole, int messageSendTag = 0) 44 { 45 //8.若是字典中存在以operation为key的项,就调用对应的抽象策略类中的AddMessageToMongo方法 46 if (dicSaveOffMessage.ContainsKey(operation)) 47 { 48 dicSaveOffMessage[operation].AddMessageToMongo(message, userRole, messageSendTag); 49 } 50 } 51 #endregion 52 }
使用反射+策略模式代替项目中大量的switch case判断的优势:blog
1.代码的扩展性好,当有新的消息类型须要处理时,只须要添加一个具体策略类进行处理便可,彻底没必要关心环境类的实现。
2.避免了建立实例过程当中的资源和时间的消耗。
缺点:
1.反射的效率较低,若是抽象策略类所在的程序集拥有的类较多时,反射效率较低的缺点就会比较明显。由于须要进行大量的循环遍历才能找到符合条件的具体策略类。