使用反射+策略模式代替項目中大量的switch case判斷

我這裏的業務場景是根據消息類型將離線消息存入mongoDB不一樣的collection中。其中就涉及到大量的分支判斷,爲了加強代碼的可讀性和可維護性,對以前的代碼進行了重構。async

先對比一下使用反射+策略模式先後的代碼:ide

重構前:

重構後:

咱們能夠看到重構前的代碼充斥着大量的分支判斷,之後每增長一個新的消息類型就要增長一個新的具體實現類和增長一個新的分支判斷,可拓展性是至關差的;而重構後的代碼當須要增長一個新的消息類型時,只須要增長一個具體的實現類就能夠實現,根本不須要考慮分支判斷,這也是咱們但願看到的。函數

接下來咱們看一下具體的實現過程:

1.抽象策略類

定義了具體策略類須要執行的具體操做,而且對外部提供了一個觸發操做的方法。抽象策略類中還定義了兩個屬性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     }

2.具體策略類

實現了抽象策略類定義的操做。在此處給抽象策略類的消息操做類型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     }

3.環境類

定義了一個字典用於存放消息類型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.反射的效率較低,若是抽象策略類所在的程序集擁有的類較多時,反射效率較低的缺點就會比較明顯。由於須要進行大量的循環遍歷才能找到符合條件的具體策略類。

相關文章
相關標籤/搜索