一:實際場景介紹
我們在給使用者做訂單催付通知的時候,會有這樣的一種場景,使用者在系統後臺設定一組可以催付的規則,比如說訂單金額大於xx元,非黑名單使用者,來自
哪個地區,已購買過某個商品,指定某個營銷活動的人等等這樣的條件,如果這時使用者在淘寶上下了一個訂單,那程式要判斷的就是看一下此訂單是否滿足這
些規則中的某一個,如果滿足,我們給他傳送催付通知,這種場景是很多做CRM的同學都會遇到的問題,那針對這種場景,如何更好的規劃業務邏輯呢?
二:普通的程式設計程式碼
在這裡我們就不考慮多篩選條件下的效能,而只從程式碼維護複雜度考慮,如果不清楚設計模式的同學,大概會寫出如下的程式碼:
1 namespace ConsoleApplication1 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 var regulars = new List<Regulars>(); 8 9 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則1", AnalysisConditons = "xxxx" }); 10 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則2", AnalysisConditons = "xxxx" }); 11 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則3", AnalysisConditons = "xxxx" }); 12 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則4", AnalysisConditons = "xxxx" }); 13 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則5", AnalysisConditons = "xxxx" }); 14 15 var filters = FilterRegularID(regulars); 16 filters = FilterRegularName(filters); 17 filters = FilterCondtions(filters); 18 19 //... 後續邏輯 20 } 21 22 static List<Regulars> FilterRegularID(List<Regulars> persons) 23 { 24 //過濾 “姓名” 的邏輯 25 return null; 26 } 27 28 static List<Regulars> FilterRegularName(List<Regulars> persons) 29 { 30 //過濾 “age” 的邏輯 31 return null; 32 } 33 34 static List<Regulars> FilterCondtions(List<Regulars> persons) 35 { 36 //過濾 “email” 的邏輯 37 return null; 38 } 39 } 40 41 /// <summary> 42 /// 各種催付規則 43 /// </summary> 44 public class Regulars 45 { 46 public int RegularID { get; set; } 47 48 public string RegularName { get; set; } 49 50 public string AnalysisConditons { get; set; } 51 } 52 }
為了演示,上面的程式碼是從regularid,regularname,condition三個維度對regulars這個聚合物件進行AND模式的篩選過濾,當過濾維度比較多的時候,這種寫
法看的出來是簡單粗暴,維護起來也必須簡單粗暴, 所以上萬行程式碼也就是這麼出來的,設計模式告訴我們一個簡單的“開閉原則”,那就是追求最小化的修改程式碼,這種
場景有更好的優化策略嗎?對應到設計模式上就是“過濾器模式”,專門針對這種場景的解決方案,一個維度一個類,然後通過邏輯運算類將他們進行組合,可以看出這是一
種“結構式的設計模式”。
三:過濾器模式
好了,廢話不多說,先來看一下優化後的設計圖紙如下:
從上面這張圖紙中可以看到,我已經將三個維度的過濾方法提取成了三個子類,由此抽象出了一個IFilter介面,當然你也可以定義成抽象類,然後實現了兩個運算級
AND和OR子類Filter,用於動態的對原子性的RegularIDFilter,RegularNameFilter,ReuglarCondtionFilter進行AND,OR邏輯運算,下面我們再看具體程式碼:
1.IFilter
public interface IFilter { List<Regulars> Filter(List<Regulars> regulars); }
2. RegularIDFilter
1 public class RegularIDFilter : IFilter 2 { 3 /// <summary> 4 /// Regulars的過濾邏輯 5 /// </summary> 6 /// <param name="regulars"></param> 7 /// <returns></returns> 8 public List<Regulars> Filter(List<Regulars> regulars) 9 { 10 return null; 11 } 12 }
3.RegularNameFilter
1 public class RegularNameFilter : IFilter 2 { 3 /// <summary> 4 /// regularName的過濾方式 5 /// </summary> 6 /// <param name="regulars"></param> 7 /// <returns></returns> 8 public List<Regulars> Filter(List<Regulars> regulars) 9 { 10 return null; 11 } 12 }
4. RegularCondtionFilter
1 public class RegularCondtionFilter : IFilter 2 { 3 /// <summary> 4 /// Condition的過濾條件 5 /// </summary> 6 /// <param name="regulars"></param> 7 /// <returns></returns> 8 public List<Regulars> Filter(List<Regulars> regulars) 9 { 10 return null; 11 } 12 }
5.AndFilter
1 /// <summary> 2 /// filter的 And 模式 3 /// </summary> 4 public class AndFilter : IFilter 5 { 6 List<IFilter> filters = new List<IFilter>(); 7 8 public AndFilter(List<IFilter> filters) 9 { 10 this.filters = filters; 11 } 12 13 public List<Regulars> Filter(List<Regulars> regulars) 14 { 15 var regularlist = new List<Regulars>(regulars); 16 17 foreach (var criteriaItem in filters) 18 { 19 regularlist = criteriaItem.Filter(regularlist); 20 } 21 22 return regularlist; 23 } 24 }
6.OrFilter
1 public class OrFilter : IFilter 2 { 3 List<IFilter> filters = null; 4 5 public OrFilter(List<IFilter> filters) 6 { 7 this.filters = filters; 8 } 9 10 public List<Regulars> Filter(List<Regulars> regulars) 11 { 12 //用hash去重 13 var resultHash = new HashSet<Regulars>(); 14 15 foreach (var filter in filters) 16 { 17 var smallPersonList = filter.Filter(regulars); 18 19 foreach (var small in smallPersonList) 20 { 21 resultHash.Add(small); 22 } 23 } 24 25 return resultHash.ToList(); 26 } 27 }
7. 最後我們客戶端呼叫程式就簡單了,只要將“原子性”的過濾條件追加到“邏輯運算類”中就完美了,如下圖:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var regulars = new List<Regulars>(); 6 7 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則1", AnalysisConditons = "xxxx" }); 8 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則2", AnalysisConditons = "xxxx" }); 9 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則3", AnalysisConditons = "xxxx" }); 10 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則4", AnalysisConditons = "xxxx" }); 11 regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則5", AnalysisConditons = "xxxx" }); 12 13 //追加filter條件 14 var filterList = new IFilter[3] { 15 new RegularIDFilter(), 16 new RegularNameFilter(), 17 new RegularCondtionFilter() 18 }; 19 20 var andCriteria = new AndFilter(filterList.ToList()); 21 22 //進行 And組合 過濾 23 andCriteria.Filter(regulars); 24 25 } 26 }
當你仔細看完上面的程式碼,會不會發現,如果後續有需求變更,比如說增加篩選的維度,我只需要新增一個繼承IFilter的子類就搞定了,客戶端在呼叫的時候只要
在Filters集合中追加該篩選維度,是不是就OK了,所以這種模式幾乎達到了無程式碼修改的地步~~~好了,本篇就說到了這裡,希望對你有幫助~