1. 程式人生 > >java設計模式分析之過濾器模式 、組合模式、外觀模式

java設計模式分析之過濾器模式 、組合模式、外觀模式

一個優秀的android開源框架中往往會體現出很多Java設計模式的影子,瞭解設計模式有助於理解開源框架中的程式設計之美接下來我會將自己整理的對一些設計模式的理解記錄在這裡

若您對我的分享感興趣可以訪問:java設計模式專欄

本篇記錄:過濾器模式 、組合模式、外觀模式

參考:
1、過濾器模式
介紹:

過濾器模式(Filter Pattern)或標準模式(Criteria Pattern)是一種結構型模式,這種模式允許開發人員使用不同的標準來過濾一組物件,通過邏輯運算以解耦的方式把它們連線起來,它可以結合多個標準來獲得單一標準

方法:

我們可以定義一系列規則。這些規則可以針對目標進行滿足自身規則的過濾

使用場景:

我們在給使用者做訂單催付通知的時候,會有這樣的一種場景,使用者在系統後臺設定一組可以催付的規則,比如說訂單金額大於xx元,非黑名單使用者,來自哪個地區,已購買過某個商品等等這樣的條件,如果這時使用者下了一個訂單,那程式要判斷的就是看一下此訂單是否滿足這些規則中的某一個,如果滿足,我們給他傳送催付通知,這種場景是很多做CRM的同學都會遇到的問題,那針對這種場景,如何更好的規劃業務邏輯呢?

如果是普通程式碼實現:

我們可能會定義出一個刪選類,由其負責對各種規則的篩選,那麼很多不清楚設計模式的人可能會秀出如下程式碼:

var regulars = new List<Regulars>();
regulars.
Add(new Regulars() { RegularID = 1, RegularName = "規則1", AnalysisConditons = "xxxx" }); regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則2", AnalysisConditons = "xxxx" }); regulars.Add(new Regulars() { RegularID = 1, RegularName = "規則3", AnalysisConditons = "xxxx" }); regulars.Add(new Regulars()
{ RegularID = 1, RegularName = "規則4", AnalysisConditons = "xxxx" }); var filters = FilterRegularID(regulars); filters = FilterRegularName(filters); filters = FilterCondtions(filters); //... 後續邏輯 } static List<Regulars> FilterRegularID(List<Regulars> persons){ //過濾 “姓名” 的邏輯 return null; } static List<Regulars> FilterRegularName(List<Regulars> persons){ //過濾 “age” 的邏輯 return null; }
    /// <summary>
     /// 各種催付規則
/// </summary>
}

    這種寫法簡單粗暴,維護起來也是一樣的簡單粗暴,上萬行的程式碼就是這樣出來的,暫時看來已經實現了必須的功能,但是一旦後續一旦有規則的變更,相信即便是當時寫出這些程式碼的人也會頭疼

設計模式告訴我們一個簡單的“開閉原則”,那就是追求最小化的修改程式碼,這種場景有更好的優化策略嗎?

    當然有對應到設計模式上就是“過濾器模式”,專門針對這種場景的解決方案,一個維度一個類,然後通過邏輯運算類將他們進行組合

讓我們看看使用過濾器模式優化之後的程式碼結構:


    從上面這張圖紙中可以看到,我們抽象出了一個IFilter介面,然後提取成了三個子類每一個子類負責一個維度的過濾方法,然後實現了兩個邏輯運算類AND和OR子類Filter,用於動態的對上面的三個維度的過濾方法進行AND,OR邏輯運算

最後在我們的呼叫程式裡面,我們需要做的工作就簡單了,只需要將每個維度的過濾條件,追加到邏輯運算類裡面就可以說笑呢功能了

and和or邏輯運算類的實現也比較簡單,兩個類裡面都維護了一個過濾器列表

  • and類只需要遍歷所有的過濾器,剔除需要過濾的原始物件集合中不符合規則的物件就可以了
  • or類可以使用一個hashMap,從需要過濾的原始物件集合中,選取符合某一個過濾條件的物件放入hashMap就可以了

使用泛型我們可以增加對多種過濾物件的支援

有沒有發現,如果後續有需求變更,比如說增加篩選的維度,我只需要新增一個繼承IFilter的子類就搞定了,客戶端在呼叫的時候只要在Filters集合中追加該篩選維度,是不是就OK了

所以這種模式幾乎達到了無程式碼修改的地步,這就是設計模式給我們帶來的便捷

2、組合模式

可能我們看到組合這個詞,會下意識的和Java物件裡面的物件的組合聯絡起來,但其實這兩者之間沒有聯絡

java物件的組合指的是物件之間的協同工作,例如:

    一個人可以由大腦、軀幹、四肢等等物件組成那麼在People這個類中將head、trunk、limb等作為欄位組合進來,讓這些物件協同完成一個人物件和以完成的工作,這就是java物件的組合

    而組合模式則主要是使用面向物件的思想來實現樹形結構的構建與處理,其中心思想是把一組相似的物件當做一個單一物件來進行處理

舉個例子:

windows系統中對於資料夾管理就是典型的組合模式對於樹形結構的處理

再比如應用軟體中的選單,辦公系統中的公司組織結構等等

組合模式的優點:

組合模式通過一種巧妙的設計方案使得使用者可以一致性地處理整個樹形結構或者樹形結構的一部分,也可以一致性地處理樹形結構中的葉子節點(不包含子節點的節點)和容器節點(包含子節點的節點)

接下來我們看看組合模式在實際場景中的應用

設計防毒軟體:

    該軟體既可以對某個資料夾(Folder)防毒,也可以對某個指定的檔案(File)進行防毒。該防毒軟體還可以根據各類檔案的特點,為不同型別的檔案提供不同的防毒方式,例如影象檔案(ImageFile)和文字檔案(TextFile)的防毒方式就有所差異。現需要提供該防毒軟體的整體框架設計方案。

     對於樹形結構,當容器物件(如資料夾)的某一個方法被呼叫時,將遍歷整個樹形結構,尋找也包含這個方法的成員物件(可以是容器物件,也可以是葉子物件)並呼叫執行,牽一而動百,其中使用了遞迴呼叫的機制來對整個結構進行處理。

    由於容器物件和葉子物件在功能上的區別,在使用這些物件的程式碼中必須有區別地對待容器物件和葉子物件,而實際上大多數情況下我們希望一致地處理它們,因為對於這些物件的區別對待將會使得程式非常複雜。

    組合模式為解決此類問題而誕生,它可以讓葉子物件和容器物件的使用具有一致性

例如我們對防毒軟體的架構設計如下:



AbstractFile充當抽象構件類,Folder充當容器構件類,ImageFile、TextFile和VideoFile充當葉子構件類

葉子類不支援檔案的增刪操作,但是因為要進行統一封裝,保持對呼叫這的透明,所以也實現了增刪操作,不過可以提供對應的錯誤提示和異常處理

    由於在本例項中使用了組合模式,在抽象構件類中聲明瞭所有方法,包括用於管理和訪問子構件的方法,如add()方法和remove()方法等,因此在ImageFile等葉子構件類中實現這些方法時必須進行相應的異常處理或錯誤提示

在容器構件類Folder類的killVirus()方法中,Folder將遞迴呼叫其成員物件的killVirus()方法,從而實現對整個樹形結構的遍歷。


如果需要更換操作節點,例如只需對資料夾“文字檔案”進行防毒,客戶端程式碼只需進行簡單的修改即可

客戶端無須關心節點的層次結構,可以對所選節點進行統一處理,提高系統的靈活性

當然如果不想讓葉子節點實現不必要的方法,可以採用安全組合模式:


    安全組合模式的缺點是不夠透明,因為葉子構件和容器構件具有不同的方法,且容器構件中那些用於管理成員物件的方法沒有在抽象構件類中定義,因此客戶端不能完全針對抽象程式設計,必須有區別地對待葉子構件和容器構件

    在實際應用中,安全組合模式的使用頻率也非常高,在Java AWT中使用的組合模式就是安全組合模式。

3、外觀模式(retrofit)
外觀模式(門面模式),這種模式隱藏了系統的複雜性,並向客戶端提供了一個客戶端可以訪問系統的介面。來隱藏系統的複雜性
例如:圖片載入框架Glide,我們只需要使用其提供的簡單的操作介面就可以完成複雜的功能,Glide用這種模式隱藏了其背後進行的大量工作
關鍵之處在於:
在客戶端和複雜系統之間再加上一層,這一層將呼叫順序,依賴關係等處理好這也就是Glide提供給我們的接待員,這個接待員理解Glide在背後所做的各種工作,而隱藏了其中的複雜聯絡,只向我們提供了簡單的呼叫入口
缺點:
不符合開閉原則,如果想修改東西會比較麻煩,因為繼承重寫都不方便

原理圖如下:


ShapeMaker就是外觀模式提供的接待員,這個接待員熟悉左邊複雜的shape系統的內部結構,然後隱藏了其中的各種實現,而僅僅只向我們提供了簡單那的呼叫方法

優點是:簡單容易上手,並且比較安全

還有一個使用場景:在軟體的各個層次結構中,可以使用外觀模式定義系統中的每一層的入口