C++實現簡單的攔截器

背景

最近看了之前某代碼的設計,深感如果需求改變,會造成很多地方需要修改代碼。因為:

當前設計嚴重依賴繼承,比如有一個類(會話)繼承了某些協議解析(用於表示這個會話採用的這種協議進行通信),繼承了某個線程基類對象(表示這個會話的邏輯處理是多線程或單線程)。

但其實這個類本身只是一個近似邏輯的業務對象,它本身不應該去關注/依賴網路協議或者線程模型。

我希望當通信協議改變或者我們要將邏輯多線程改成單線程(亦或是從單線程邏輯改為多線程)時,以及需要增加一些功能的時候,這個類本身並不需要做任何改變。

恰好最近看到一些開源庫,從中得到了一些提示,比如通過 inteceptor 來組合功能,形成一個流水線,一步步完成各個子功能,而不需要繼承(也就是需求改變的時候,我們只需要移除某些先前註冊的 inteceptor,而無需改變繼承方式,也減少了類型耦合)

實現

當有了這個想法之後,我考慮如何用C++實現它 (跟 sumory/lor 中的 middleware 有點類似) 。我要的大概形式如下:

Inteceptor inteceptor; inteceptor.childHandler([](Context& context, const std::string& str, const INTERCEPTOR& next) { next(context, str + "1"); }).childHandler([](Context& context, const std::string& str, const NTERCEPTOR& next) { next(context, str); }).childHandler([](Context& context, const std::string& str, const INTERCEPTOR& next) { std::cout << "Well, Youre finally here." << std::endl; }); inteceptor.handler("test")

1、每一個 childHandle 具備一個next 回調參數(它不具備返回值),並且很容易看出來,childHandler 和 next 的類型是不一樣的。

2、顯式的由用戶去調用next,這樣用戶可以控制傳遞給它的參數,並且提供 Context,可以在某些 childHandler 里添加kv,然後在後續的 childHandler里取用。

3、不需要調用 finish 函數!(用於表示結束註冊 childHandler)

(這樣,我們可以在某些handler里unpack,然後解密,然後日誌記錄,然後交予邏輯對象的消息處理,注意Inteceptor本身並不具備和包含任何網路或其他功能,你需要在 childHandler去填充你的邏輯)

這裡面其中的難點是1和3。

其中 1 的難點是兩種回調類型不一樣,當調用 next後其實是需要調用用戶下一個所註冊的 childHandler 。

其中 3 的限制則不允許我們預先準備好一個 vector<HANDLER>,然後根據它生成一個調用鏈(類似list, next->next->next)。

(其中1 稍容易解決)

當我第一次想實現它的時候,在家電腦前比划了半個多小時,沒有突破,因為無論如何我都不能把多次 childHandler的參數給串聯起來。然後就去睡覺。在床長我閉著眼睛想了幾分鐘:

(友情提示,如果覺得這個需求有意思,或者有點小難度,可以先跳過下面我的思維過程,不妨先自己想想)

1、我先繼續想了下現在的方案怎麼才能實現。無果。

2、我轉變了思路,既然由於1、3難點導致我現在想不出來(主要是3),那麼我不妨看看如果去掉最難的 3 這個限制後該怎麼做(也就是允許類似 github.com/facebook/wan 里的 finalize 調用,當然它裡面用的OO來表示Handler,跟我想的用法有不一樣)。

哦,無非是它能夠一次性拿到所有的childHandler放到vector里,然後再遍歷它們(這就很容易知道某一個HANDLER的後繼HANDLER),進行串聯起來形成一個鏈。

3、因此我的方案難點在於我每次 childHandler 時,我並不知道後繼,因此我不知道該怎麼把兩個相鄰的 childHandler 串起來。

4、既然先天性的無法在註冊某個 childHandler 時去掛接它的後繼(因為這個時候沒有後繼),但卻能夠給後繼預留位置,先給它佔一個位置,等它來的時候再讓它坐下!

5、顯然,我一下子想到了 C/C++的精髓。

於是睡等天亮,再坐等下班,再打開電腦用了10分鐘完成它。

拓展

今天我初略用它改了改第一段里提高的代碼,果然依賴和繼承去除了,而且更容易適應需求變更,然後我在想,這到底是有什麼魔法?於是想到了一個名字:AOP (zh.wikipedia.org/zh-han)。

老實說以前聽說過也看過它,但可能當時知道它的意思,但並不能說從骨子裡理解了。(畢竟一直覺得我們真正學會東西的最好方式是先用了不好姿勢,然後進坑,然後改進到好的方式,才能更好的 - 比起直接看會好的姿勢 - 理解更深刻)

其他

代碼實現的話,如果覺得有意思,可以嘗試實現下,可以評論里交流哦,如果真有朋友想要實現,可以告訴我。(因為我不太好意思把實現貼在這裡)

或者說這個 Inteceptor 在使用姿勢(設計)上就有問題,可以告訴我哈。(比如我自己就知道一個,childHandler 的第二個參數更應該至少是一個 Buf 而不是 std::string )


推薦閱讀:

小議webpack下的AOP式無侵入注入
Spring AOP中定義切點(PointCut)和通知(Advice)

TAG:面向对象编程 | AOP | 网络编程 |