從真實專案中摳出來的設計模式——第二篇:過濾器模式

一線碼農發表於2017-02-19

一:實際場景介紹

     我們在給使用者做訂單催付通知的時候,會有這樣的一種場景,使用者在系統後臺設定一組可以催付的規則,比如說訂單金額大於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了,所以這種模式幾乎達到了無程式碼修改的地步~~~好了,本篇就說到了這裡,希望對你有幫助~

 

相關文章