代碼復用在軟件開發中存在兩個層次。第一個層次是,在設計一個新的軟件功能或是開發一個新的項目時,復用已存在的軟件模塊,這種復用或許稱之為設計復用更好。另一個層次是,程序員在開發一個軟件模塊時,模塊的內部應盡可能地復用。從編程習慣的角度來看,這裡指的是後者。
現在假設存在一個雙向鏈表Double-Linked List, DLL)的一個模塊,如果這個模塊在開發的過程中,已經存在了兩個函數,分別是dll_push_tail()和dll_pop_head(),這兩個函數的作用分別是將一個新的節點加入到鏈表的尾部以及從鏈表中刪除並返回頭節點。其代碼實現如圖1所示。
dll.c
00088: void dll_push_tail (dll_t *_p_dll, dll_node_t *_p_node)
00089: {
00090: if (0 ==_p_dll->tail_) {
00091: _p_dll->head_ = _p_dll->tail_ = _p_node;
00092: _p_node->next_ = _p_node->prev_ = 0;
00093: }
00094: else {
00095: dll_node_t *p_tail = _p_dll->tail_;
00096:
00097: p_tail->next_ = _p_node;
00098: _p_node->prev_ = p_tail;
00099: _p_node->next_ = 0;
00100: _p_dll->tail_ = _p_node;
00101: }
00102:
00103: _p_dll->count_ ++;
00104: }
00105:
00106: dll_node_t *dll_pop_head (dll_t *_p_dll)
00107: {
00108: dll_node_t *p_node = _p_dll->head_;
00109:
00110: if (p_node != 0) {
00111: _p_dll->count_--;
00112: _p_dll->head_ = p_node->next_;
00113: if (0 ==_p_dll->head_) {
00114: _p_dll->tail_ = 0;
00115: }
00116: else {
00117: p_node->next_->prev_ = 0;
00118: }
00119: }
00120:
00121: return p_node;
00122: }
圖1
如果此時需要增加一個新的鏈表操作函數dll_merge(),用於合並兩個鏈表。則這個函數的實現可能如圖2所示。其思路也很簡單,就是從_p_src鏈表中將一個個的節點取出並放到_p_dest鏈表的尾部。
dll.c
00165: void dll_merge (dll_t *_p_dest, dll_t *_p_src)
00166: {
00167: dll_node_t *p_node = _p_src->head_;
00168:
00169: while (0 != p_node) {
00170: if (0 ==_p_dest->tail_) {
00171: _p_dest->head_ = _p_dest->tail_ = p_node;
00172: _p_dest->next_ = _p_dest->prev_ = 0;
00173: }
00174: else {
00175: dll_node_t *p_tail = _p_dest->tail_;
00176:
00177: p_tail->next_ = _p_dest;
00178: _p_dest->prev_ = p_tail;
00179: _p_dest->next_ = 0;
00180: _p_dest->tail_ = _p_dest;
00181: }
00182:
00183: _p_dest->count_ ++;
00184: p_node = p_node->next_;
00185: }
00186:
00187: _p_src->count_ = 0;
00188: _p_src->head_ = 0;
00189: _p_src->tail_ = 0;
00190: }
圖2
有問題嗎?從功能性的角度來說沒有問題,但是從可維護性方面來看,這一實現並不好,取而代之的更好實現是通過代碼復用的方式,如圖3所示。
dll.c
00175: void dll_merge (dll_t *_p_dest, dll_t *_p_src)
00176: {
00177: dll_node_t *p_node = dll_pop_head (_p_src);
00178:
00179: while (0 != p_node) {
00180: dll_push_tail (_p_dest, p_node);
00181: p_node = dll_pop_head (_p_src);
00182: }
00183: }
圖3
顯然,采用代碼復用的方式,其可讀性更好,也更容易維護。在實現一個軟件模塊時,應當考慮從所需實現的功能中抽取出一些公共的基本函數比如,這裡談到的dll_pop_head()和dll_push_tail()),且這些函數所實現的功能是正交的即功能沒有重疊)。接下來,其它的功能比如這裡談到的dll_merge())可以考慮采用搭積木的方式,通過運用那些最基本的函數去實現。
需要注意的是,采用復用方式實現的dll_merge()引入了函數調用,而函數的調用因為存在參數的傳遞可能會帶來一定的處理器開銷,其開銷的大小與處理器的處理能力有關。但是,對於現代的大多處理器來說,這種開銷都是很小的,且小到可以幾乎忽略不計。另外,如果要去除函數調用所帶來的開銷,可以考慮采用inline的方式。拿這裡的dll_merge()的實現為例,如果dll_push_tail()和dll_pop_head()被定義為inline的話,則dll_merge()中調用這兩個函數就完全不存在函數調用的開銷了。
本文出自 “至簡李雲” 博客,請務必保留此出處http://yunli.blog.51cto.com/831344/263932