手機探索者開發實錄—資料解包
資料打包比較容易,解包卻要困難得多。XML解析有DOM和SAX兩種方式,我比較喜歡SAX方式,一是比較簡單,不需要熟悉複雜的DOM API。二是可以邊解析邊處理。三是開源的expat簡單易用,而且支援UTF-8編碼。是以在手機探索者中,我們理所當然的采用SAX方式了。SAX是典型的builder模式,有了expat的幫助,我們要做的就是實作一些builder。
為了提高靈活性,也可以說是自找麻煩,我們允許同一個資料包中封裝多個請求/響應/事件,不同請求的參數不同,是以build方式也是不同的,如果把這些請求的build放在一起,代碼會顯得到複雜。怎麼辦呢,在這裡,我第一次發現狀态模式有了用武之地,不同的請求不就相當于不同的狀态嗎,每解析到一個請求,我們就把目前builder切換到相應的builder上,這就相當于狀态切換。可以建立一個查找表,根據名稱找到相應的builder,這樣邏輯上很清晰。
每個builder的實作很簡單,畢竟裡面的參數不多,不過要去寫幾十個builder很難說是件有趣的事。我喜歡單調的工作,因為我知道單調的工作總是有規律可循,找到這些規律就找到了解決問題的捷徑。做了簡單的分析之後,我發現參數的類型主要有三種:一是基本類型,像整數和字元串,二是結構,三是數組。其它所有類型都可以用這三種組合起來,于是就寫了三個builder分别處理這三種類型。然後隻要幾行代碼就可以為一個請求或者響應組合成一個builder。
Builder的接口定義如下,基本上是expat需要的接口:
- struct _MobileExplorerBuilder;
- typedef struct _MobileExplorerBuilder MobileExplorerBuilder;
- typedef MeRet (*MobileExplorerBuilderSetContextFunc)(MobileExplorerBuilder* thiz, void* ctx);
- typedef MeRet (*MobileExplorerBuilderOnStartFunc)(MobileExplorerBuilder* thiz, const char* name, con
- st char** atts);
- typedef MeRet (*MobileExplorerBuilderOnTextFunc)(MobileExplorerBuilder* thiz, const char* text, size
- _t length);
- typedef MeRet (*MobileExplorerBuilderOnEndFunc)(MobileExplorerBuilder* thiz, const char* name);
- typedef MeRet (*MobileExplorerBuilderDestroyFunc)(MobileExplorerBuilder* thiz);
- struct _MobileExplorerBuilder
- {
- MobileExplorerBuilderSetContextFunc set_context;
- MobileExplorerBuilderOnStartFunc on_start;
- MobileExplorerBuilderOnTextFunc on_text;
- MobileExplorerBuilderOnEndFunc on_end;
- MobileExplorerBuilderDestroyFunc destroy;
- char priv[0];
- };
雖然我們确定了用expat,它是開源的,跨平台的,也很好用。不過我還是不喜歡把自己綁定特定的函數庫上,我決定它寫一個parser函數,它隻是簡單的包裝expat,使用起來更簡單,也隔離了expat。
~~end~~