【編程好習慣】複用代碼以提升可維護性

代碼複用在軟件開發中存在兩個層次。第一個層次是,在設計一個新的軟件功能或是開發一個新的項目時,複用已存在的軟件模塊,這種複用或許稱之爲設計複用更好。另外一個層次是,程序員在開發一個軟件模塊時,模塊的內部應儘量地複用。從編程習慣的角度來看,這裏指的是後者。

如今假設存在一個雙向鏈表(Double-Linked List, DLL)的一個模塊,若是這個模塊在開發的過程當中,已經存在了兩個函數,分別是dll_push_tail()和dll_pop_head(),這兩個函數的做用分別是將一個新的節點加入到鏈表的尾部以及從鏈表中刪除並返回頭節點。其代碼實現如圖1所示。
node

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()中調用這兩個函數就徹底不存在函數調用的開銷了。
ide

相關文章
相關標籤/搜索