1. 程式人生 > >設計模式-builder模式(以微信訊息的路由處理為例)

設計模式-builder模式(以微信訊息的路由處理為例)

今天要講一個簡單的模式–builder模式。
你可能會覺得,builder模式有什麼好講的?本來我也這樣覺得,但當我有幸拜讀某位大神通過builder模式寫了一個開發工具包的初始化操作,嗯程式碼的樣子很叼
這位大神是誰呢?
在這裡插入圖片描述
就是這位仁兄啦,是碼雲上一個很火的微信開發工具包的貢獻者之一。

在這裡插入圖片描述
一、言歸正傳,讓我們來先看下業務:
(1)目標:
對微信端傳來的使用者訊息進行處理。
(2)需要解決的問題:
訊息型別多:微信推送給公眾號(即繫結的應用伺服器)的訊息型別很多,我們需要針對使用者不同的輸入做出不同的反應。
(3)一般的解決方式:
if(msgType.equals(“text”)){
doSomething();
} else if(msgType.equals(“image”)){
doSomething()
} else if(msgType.equals(“event”)){
if(event.equals(“subscribe”)){
doSomething();
}else if(event.equals(“click”)){
doSomething();
}

}

嗯,很low的寫法~

二、知道了業務和一般的實現方式後,我想你對需要做的事有了一定的瞭解,現在來看看大神是怎麼實現多種型別的訊息的處理的。

1.先定義一個訊息路由器(簡稱Router),他是“訊息處理器”的抽象。
2.為了實現“開閉原則”和“單一職責”的設計原則,“處理訊息”這個功能肯定要單獨拿出來,將之定義為MessageHandler
3.然後我們也不能對所有的訊息都做處理,所以肯定需要過濾器,在這裡我們將過濾器稱之為規則(Rule),即符合規則的就進行處理。

所以,從邏輯上來看,大體就是“訊息處理器”(Router)包含MessageHandler和Rule。在最外層,我們使用Router進行訊息處理。

不過,這麼理解其實是有錯誤的,哈哈~~
再仔細思考下流程,我們應該是先對訊息進行規則的匹配,匹配通過了,才對訊息進行處理。所以應該是Rule包含MessageHandler。

那麼整體的結構就是:Router包含Rule,而Rule又包含MessageHandler。

三、接下來就是程式碼構建環節,先看看到底是怎麼使用的吧。
在這裡插入圖片描述

可以看到我們在微信的配置類裡建立了一個Router物件,進行統一的訊息路由處理。
然後Router物件是怎麼建立的呢?
可以看到,我框出來的2個核心方法rule()和handler()。rule()就是建立匹配規則,對訊息進行過濾(之後的msgType()、event()就是表明對哪個特定型別的微信事件進行規則的匹配);而handler()就是將某個特定的處理器賦予規則,讓規則匹配的時候可以對訊息進行相應的處理。

看到這你可能會問,rule和handler是有了,那它們是怎麼賦值給Router物件的呢?

答案就是鏈式呼叫最後的next()或end()方法。

先來看看rule()方法到底幹了什麼吧~
Router.rule()方法如下:
在這裡插入圖片描述
可以看到,router再建立rule物件的時候,把router自己給傳了進去。

再來看rule物件的next()或end()方法:

在這裡插入圖片描述
在這裡插入圖片描述
可以看到next()就是呼叫了end()方法,只是把reEnter屬性設定為true(reEnter預設為false),表示可以繼續處理下面的規則,即對訊息進行多重處理(如日誌規則處理完後再做其他業務規則的匹配)
當然啦,最核心的就是呼叫了Router物件getRules()方法,將自己(rule物件)傳遞給Router物件。
在這裡插入圖片描述
可以看到,Router物件裡維護了一個Rule陣列。

這樣,Router物件就“不知不覺”間有了很多的rule物件,而各個rule物件設定了不同的規則屬性和handler物件,整個Router就豐滿了起來~

到這裡,微信訊息路由的設計已經講的差不多了,再補充幾個小的知識點:
1.builder模式的一個特徵就是使用鏈式呼叫,即設定屬性的方法的返回值是物件本身,而不是void,舉個例子:

在這裡插入圖片描述
這裡Rule物件的msgType()和event()方法就是設定屬性的方法,而返回的Rule物件本身,這樣就可以達到鏈式呼叫。

2.可以看到,Rule物件最後的end()或next()方法返回的是Router物件,所以還可以這樣鏈式呼叫:
在這裡插入圖片描述
就是這樣一直“點”下去,哈哈。當然了,這樣的呼叫沒有之前那麼清晰,就不用刻意地鏈式呼叫下去了,這裡只是為了演示鏈式呼叫的做法。

3.你可能還會好奇規則(rule)到底是怎麼匹配微信傳來的訊息的,其實也很簡單:
在這裡插入圖片描述
在這裡你可以看到匹配規則的收集和規則的重入(之前說的reEnter屬性),reEnter為false的時候只會收集自己這一條規則。(所以rule在list中的順序也有講究哦:配置路由規則時要按照從細到粗的原則,否則可能訊息可能會被提前處理)
言歸正傳,Router物件要使用哪條規則其實是看Rule物件的test()方法。再來一起看看它:
在這裡插入圖片描述
可以看到,就是先看Rule物件自己有沒有這些屬性。若有其中某些屬性,如:fromUser、msgType等,說明這些屬性就是要關注的匹配項,如果後面的this.fromUser.equals(wxMessage.getFromUser())或this.msgType.equalsIgnoreCase(wxMessage.getMsgType())為false,說明匹配不通過,這條微信訊息不適用這條Rule來處理。
如果Rule物件一條屬性都沒有,那麼這個test是匹配通過的!(通過程式碼可以容易看出,所有”&&”關聯的都為true)

總結下來,你為Rule物件設定的屬性越多,那這就是一個越細分場景的處理規則;相反,若你一個屬性都不設定,那這就是一個通用的規則。(如:日誌規則,只設置了個hander)
在這裡插入圖片描述

今天就講到這,歡迎大家評論交流~