模式名稱
- SAD, Simple API for Datagram
意圖
- 分離網路報文的解析和處理, 使解析代碼和處理代碼不再耦合在一起, 便於擴充. 類似SAX(Simple API for XML)將XML文檔的解析和處理分離到不同的單元中
動機
在網路通訊軟體的開發中, 經常要處理網路上接收到的各種資料報文. 而收到某種報文後, 需要進行的處理邏輯上可能不止一件事情. 處理過程中會用到報文中的資料, 因此需要對報文進行解析. 而報文的結構通常存在動態部分, 而在C語言中, 無法定義一個資料結構可以直接將報文映射到該結構. 參見<<Navigator Pattern: 導航者模式>>
缺乏考慮的做法通常會把解析和處理放在一起, 一個大函數, 用局部變數甚至全域變數來儲存解析出來的資料, 並對其進行各種處理. 這樣做的問題是:
- 難以擴充: 當需要增加新的處理時, 需要在解析過程中多個地方插入處理代碼
- 難以理解: 不同的處理代碼混在一起, 和報文解析的邏輯也混在一起, 難以看清楚真正做了什麼事
- 容易出錯: 不同的處理共用解析出來的資料, 容易互相影響, 引入錯誤
另外一種常見的做法是每種不同的處理單獨去解析自己需要的內容. 這種方式相對內聚, 但需要解析多遍報文結構, 解析代碼也有重複
我們需要更好的設計.
方案
SAX以事件驅動的方式分離了XML文檔的解析和處理. 我們可以借鑒. 報文有內部結構, 我們可以使用Navigator模式遍曆其內部結構, 並在每一個獨立的淨荷開始和結束時觸發回調, 而對報文內容的各種處理可以以回呼函數的形式註冊到解析過程中, 為每種處理編寫單獨的回呼函數.
例如, 對於Navigator模式中定義的報文結構, 可以定義如下的API:
typedef void (*MessageHandler)(Message*);typedef void (*TopLevelPayloadHandler)(TopLevelPayload*);typedef void (*SecondLevelPayloadHandler)(SecondLevelPayload*);typedef struct Handler { MessageHandler start_handle_message; MessageHandler end_handle_message; TopLevelPayloadHandler start_handle_toplevel_payload; TopLevelPayloadHandler end_handle_toplevel_payload; SecondLevelPayloadHandler start_handle_secondlevel_payload; SecondLevelPayloadHandler end_handle_secondlevel_payload;} Handler;void parse(Message* message, Handler* handlers, int handler_count) { for(int i=0; i<handler_count; i++) { handlers[i]->start_handle_message(message); } //遍曆Message內部嵌套的payload, 並調用對應的handler, 比如: //handlers[i]->start_handle_toplevel_payload(toplevel_payload_pointer); //handlers[i]->end_handle_toplevel_payload(toplevel_payload_pointer); for(int i=0; i<handler_count; i++) { handlers[i]->end_handle_message(message); }}
而每種不同的處理, 只需提供自己的handler即可. 比如可以有列印報文內容的handler, 有根據報文操作硬體的handler, 有持久化報文資料的handler等:
Handler handlers[3] = { DataPrinter, HardwareManipulator, DataPersister};parse(message, handlers, sizeof(handlers)/sizeof(handlers[0]));
效果
- 報文解析和報文處理的代碼徹底分開, 不再糾纏在一起
- 可以很容易的擴充新的報文處理邏輯
- 報文只需解析一遍
- 其約束在於不同的handler之間不應該有依賴
相關模式
- SAX是處理XML的一種類似的模式, 但其最初的出發點是源於DOM的效能太差, 不過它也有分離解析和處理的效果
- Visitor模式用於在不改變階層的情況下增加對這個階層的處理, 並且自動分發正確的處理到正確的節點. 它客觀上也分離了資料的解析和對資料的處理.