張曉宇
?
提高代碼可重用性的研究
張曉宇
摘 要:軟件公司提高收益的途徑有多種多樣,對于軟件開發(fā)人員而言,降低軟件開發(fā)成本無疑是最簡單的一條。提高代碼可重用性就是眾多降低軟件開發(fā)成本的方法中最實用的一種方式。在代碼可重用性高項目中,當需求來臨時,開發(fā)人員能夠利用系統(tǒng)中已有的元素,付出盡可能小的努力來應對這個變化。對如何提高代碼可重用性展開了研究分析。
關鍵詞:軟件開發(fā);降低成本;可重用性
對于一個企業(yè)而言,軟件開發(fā)的意義在于滿足客戶需要,然后通過滿足客戶需要,為自己所在的企業(yè)直接或者間接創(chuàng)造收益。根據(jù)“收益-成本=利潤”這條簡單的商業(yè)邏輯可以看出:為了獲取利潤最大化,企業(yè)要么提高收益,要么降低成本。提高收益的方法包括好的市場戰(zhàn)略、具有競爭力的定價和提高入門門檻等。降低成本的方法包括減少管理成本,不去購買沒有必要的設備,租賃更便宜的辦公室等。但是,這些都不是軟件開發(fā)團隊所關注的,軟件開發(fā)團隊需要關注的是如何降低軟件開發(fā)過程中的成本。軟件開發(fā)過程中的成本包括兩部分:內(nèi)在成本(Essential Cost)[1]和偶發(fā)成本(Accident Cost)。內(nèi)在成本是指解決一個問題的最簡單方案所付出的成本。偶發(fā)成本則是指為解決一個問題的方案相對于最簡單方案多出的那部分成本。
當一個需求來臨的時候,如果我們能夠盡可能地利用已有的元素,我們就可以付出盡可能小的努力來應對這個需求,這就是重用。但如果一些本來應該可以重用的部分,無法做到重用,則必須從頭做起。這些工作量重疊的部分就屬于偶發(fā)成本。因此我們可以得出這樣的結論:在開發(fā)人員實現(xiàn)需求時,如果原有系統(tǒng)對于要實現(xiàn)的需求可重用部分越少,那么開發(fā)人員為了實現(xiàn)需求需求所要付出的成本就越高。反之,提高原有系統(tǒng)代碼的可重用性可以大幅降低軟件的開發(fā)成本。
1.1 重用的誤區(qū)
重用是整個軟件開發(fā)社區(qū)幾十年來一致努力的重要課題。然而很多開發(fā)人員會錯誤地將“修改”和“拷貝粘貼”當作重用。
當要實現(xiàn)一個需求時,一種常見的方式是:在原有函數(shù)里面塞入幾行新的代碼邏輯。這樣的動機絕對是為了重用原有流程中已經(jīng)存在的代碼。這樣的做法從短期來看最簡單。甚至在很多情況下,它也是成本最少的實現(xiàn)方式。但是這樣做的不良后果就是:原有函數(shù)的邏輯很快變得復雜,進而晦澀難懂。從長期來看,團隊需要付出更多的成本來理解與維護它。另外,一個經(jīng)過修改的軟件單位,無論是它自身還是調(diào)用它的代碼,都必須重新進行驗證。如果一個軟件單位的修改內(nèi)容包括接口部分,就會造成所有使用它的代碼的修改和更大范圍的重新驗證,那么將會造成更多的偶發(fā)成本。
另外一種常見的誤區(qū)是:拷貝粘貼。為了不影響原有功能,將代碼進行拷貝粘貼,得到另外一份拷貝。然后在拷貝代碼上修改其中一部分,得到另外一段代碼。兩段代碼對應于兩種不同的需求,同時存在于同一系統(tǒng)。雖然這種動機也是重用,但造成的結果是重復。它的危害甚至比“修改”更大。
由此可見,“修改”和“拷貝粘貼”都不是真正的重用。
1.2 重用的含義
一個已經(jīng)存在的軟件單位,無需任何修改,無需拷貝粘貼就可以用于幫助實現(xiàn)某些需要,這樣的方式才是真正的重用。如果一個軟件單位必須被修改,才能滿足新的需要,即便你只修改了其中的1%,雖然看起來好像復用了其中的99%。我們?nèi)匀环Q對于這個需求不可重用。如果這個軟件單位無須修改的部分中,能夠與需要修改的部分進行分離,那么無須修改的部分對于這個需求就是可重用的。
1.3 重用的范圍
一個可重用的實體,只能在可見的范圍內(nèi)被重用。當定義一個實體的時候,希望它在多大的范圍內(nèi)被重用,決定了它的部署位置,進而影響系統(tǒng)的類設計和結構設計。如果一個可以在更大范圍內(nèi)重用的單位被控制在較小的范圍內(nèi),那么勢必會造成重復。因為這些可重用的單位會在不同的模塊中被重復定義。或者一個可重用單元U被模塊A定義,其初衷是僅供本模塊內(nèi)部使用,但卻沒有對U進行訪問范圍控制。模塊A被定義了明確的職責,但由于U的公開,模塊A需要提供更多職責。當另一個模塊B需要這個功能時候,就可以直接調(diào)用。這樣B對A就產(chǎn)生了依賴。但是這種依賴很顯然是不恰當?shù)摹?/p>
1.4 可重用性
一個軟件單位適用的范圍越廣泛,那么它被重用的可能性就越高。重用的可能性就稱作可重用性。軟件系統(tǒng)中沒有絕對可重用的東西。只有對于合適的問題,一個軟件單位才可以被重用。
從上文論述可知,重用對于降低成本具有重要意義。如何提高軟件的可重用性,Larry Constantine和Ed Yourdon在其著作《結構化設計》里給出了答案,即“高內(nèi)聚,低耦合”。本章節(jié)提到的正交策略可以幫助開發(fā)人員做到“高內(nèi)聚,低耦合”。
2.1 內(nèi)聚
內(nèi)聚,用來衡量一個單位內(nèi)部各種元素之間的關聯(lián)緊密程度。元素之間關聯(lián)度越高,則其內(nèi)聚度越高。
一方面,高內(nèi)聚強調(diào),緊密關聯(lián)的事物應該放在一起。這會增強一個軟件單位的可重用性和可理解性。另一方面,高內(nèi)聚強調(diào),只有緊密關聯(lián)的事物才應該被放在一起。把上述兩個方面結合起來,就是著名的Unix設計哲學:“Do One Thing,Do it well”。
2.2 耦合
耦合,用來衡量單位之間的關聯(lián)程度。單位之間的關聯(lián)度越高,則他們之間的耦合度就越高。耦合產(chǎn)生的原因是依賴。對于任意兩個代碼元素,當一方依賴于另一方,則兩者之間就產(chǎn)生了耦合。耦合帶來的問題是,對于耦合雙方,當被依賴的一方發(fā)生變化時,會導致依賴的一方也跟著變化。這就會導致可重用性降低。例如在圖中的系統(tǒng)中,如圖1所示:
圖1 耦合的變化
由于一個需求的來臨,需要修改D。由于D 變化,會引起A變化。又由于B和C都依賴于A,A的變化又會引起B(yǎng)和C的變化。
這樣一個需求的發(fā)生就會導致整個系統(tǒng)都會發(fā)生變化。原有系統(tǒng)對于這個新需求的可重用性就很差。同樣的,如果圖1中任何一個依賴關系得到減弱,那么D變化時,引起整個系統(tǒng)變化的概率就會降低。那么系統(tǒng)的可重用性也就會得到增強。
高耦合會降低系統(tǒng)重用性的原因在于,一個模塊直接或者間接依賴的其他代碼元素越多,為了重用它,需要引入的元素就越多,而這些元素會進一步的依賴其他元素,而那些元素中的很多東西可能并不是所需要的。另外,一個系統(tǒng)的耦合越強,那么系統(tǒng)的可理解性就越差。比如,一個函數(shù)直接引用了另外一個模塊的全局變量,開發(fā)人員必須了解,有哪些會模塊在什么樣的場合下會修改這個全局變量。只有這樣,當前函數(shù)的行為以及它所可能導致的結果才能被理解。2.3正交策略
一個低內(nèi)聚的系統(tǒng),通常是高耦合的系統(tǒng)。高度耦合會使得系統(tǒng)可重用性很低。一塊代碼的變更會很容易導致軟件系統(tǒng)大面積的修改。我們希望的情況恰恰相反,即一塊代碼的修改不會引起其他代碼的修改。這樣的狀態(tài)被稱作“正交”。正交這個詞來源于數(shù)學概念:兩個向量的積為零,則認為他們是正交的,即兩個向量是垂直的。在一個正交系統(tǒng)里,一段代碼沿著一個方向變化,但另外一個方向卻不會發(fā)生變化。一個系統(tǒng)“高內(nèi)聚,低耦合”的程度越高,其正交性越強。
為了達到較好的正交性,可以采用以下策略:
①消除重復
②分離關注點
③縮小依賴范圍
2.3.1消除重復
重復就意味著:同一件事,需要被反復的機械的執(zhí)行。它增加了代碼量,從而增加了不必要的理解成本和維護成本。重復分為完全重復和部分重復。完全重復是指系統(tǒng)中有兩段代碼,它們分別實現(xiàn)了一個功能。對于完全重復,直接刪除其中一個,保留另外一個即可。但是大多數(shù)情況下,重復是以部分重復的形態(tài)出現(xiàn)的。兩個部分重復的函數(shù)可以歸納為三種形式:參數(shù)型重復、調(diào)用型重復和回調(diào)型重復。
參數(shù)型重復是指,兩個函數(shù)的算法相同,只是處理的數(shù)據(jù)不同。消除這樣的重復,只需要將差異的數(shù)據(jù)參數(shù)化即可。
消重前:
void* f1(void* buf, const Packet* packet)
{
strcpy(buf, packet->src_address);
return (void*)((char*)buf + strlen(packet->src_address) + 1);
}
void* f2(void* buf, const Packet* packet)
{
strcpy(buf, packet->dest_address);
return (void*)((char*)buf + strlen(packet->dest_address) + 1);
}
消重后:
void* appendStrToBuf(void* buf, const char* str)
教師面對的教育對象個體因家庭環(huán)境、社會背景、智力水平、性格氣質等不同,對他們的教育手段和教育方法策略就要有所不同。要因時制宜、因生制宜,就是常說的因材施教。學生在不同的階段有不同的特點,要在教育過程中有靈活的應變能力。要了解每個教育對象的個性特征,做到有針對性,有的放矢。
{
strcpy(buf, str);
return (void*)((char*)buf + strlen(str) + 1);
}
void* f1(void* buf, const Packet* packet)
{
return appendStrToBuf(buf, packet->src_address);
}
void* f2(void* buf, const Packet* packet)
{
return appendStrToBuf(buf, packet->dest_address);
}
調(diào)用型重復是指,如果兩個函數(shù)重復部分完全相同,可以將重復的部分提取出來,然后原函數(shù)分別對它進行調(diào)用。
消重前:
void* f1(void* buf, const Packet* packet){}
void* f2(void* buf, const Packet* packet){}
消重后:
void F(){}
void* f1(void* buf, const Packet* packet){ F();}
void* f2(void* buf, const Packet* packet){F();}
回調(diào)型重復是指,如果兩個函數(shù)重復部分完全相同??梢詫⒅貜偷牟糠痔崛楹瘮?shù)F,將差異的部分修改成與原型相同的兩個函數(shù)S1和S2,然后通過F分別對S1和S2進行調(diào)用。
消重前:
void* f1(void* buf, const Packet* packet){}
void* f2(void* buf, const Packet* packet){}
消重后:
void s1(){}
void s2(){}
void F(void (*s)()){s();}
void f1(){F(s1);}
void f2(){F(s2);}
2.3.2分離關注點
消除重復是提高可用性的被動型策略。即在重復出現(xiàn)之后,不影響實現(xiàn)需求的前提下,盡可能地消除它。而消除重復,既讓整個系統(tǒng)更加干凈,又提高了系統(tǒng)內(nèi)部元素的可重用性。但是,如果系統(tǒng)在沒有重復的情況下,我們需要通過主動出擊,在早期就讓系統(tǒng)更加干凈。分離關注點就是其中一種方法。
分離關注點的好處很多,首先,是為了管理復雜度。當把一個復雜的事情分離成一個個的關注點時,每個關注點都更簡單,因此更容易理解。其次,是為了可重用性。在一個大型的網(wǎng)絡系統(tǒng)中,某個網(wǎng)元承擔了某個職責后,其他網(wǎng)元就不需要各自完成相應的職責,而是重用此網(wǎng)元的功能實現(xiàn)。分離關注點要分到什么程度,Uncle Bob在《敏捷軟件開發(fā):原則,設計與模式》里給出了答案,即符合單一職責原則。單一職責原則是指每個職責高度內(nèi)聚,不同職責松散耦合,從而讓各個職責盡可能獨立地變化,這樣就能做到分離關注點的目的。
2.3.3縮小依賴范圍
所謂耦合,是指雙方或者多方通過某種方式產(chǎn)生了依賴關系??s小依賴范圍就是指縮小它們之間的依賴關系。當談起一個某塊依賴于另一個模塊時,并非意味著一個模塊依賴對方所有的功能和代碼。這種依賴往往是局部的。盡管其依賴關系可能非常復雜和混亂。但如果仔細梳理,我們就會得出結論,在被依賴的模塊中,某個元素一旦發(fā)生變化,就會導致依賴方的變化,那么這些元素就被稱作依賴點。依賴點就是一個知識點,一個模塊了解另外一個模塊的知識點越多,則它對另外一個模塊的依賴點就越多。任意一個依賴點的變化都會導致依賴方的變化。所以兩個模塊之間存在的依賴點越多,則依賴方變化的概率就越高,這會導致糟糕的正交性。所以必須盡可能減少依賴點的數(shù)量,這就是所謂的縮小依賴范圍。
本文對如何提高代碼可重用性展開研究分析,提出了正交策略。正交策略可以有效提高軟件系統(tǒng)的內(nèi)聚度,降低耦合度,進而提高軟件的可重用性??芍赜眯愿叩能浖到y(tǒng)可以在需求變更的長期演進過程中為企業(yè)節(jié)省成本,提高企業(yè)收益。
參考文獻
[1] JoshuaKerievsky.重構到模式[M].Addison-Wesley Professional,2005.
[2] Paul M.Duvall, Steve Matyas. Andrew Glover-Contin -uous integration improving software quality and redu -cing risk[M].Addison-Wesley Professional,2007(7).
[3] 張海藩.軟件工程[M].北京:清華大學出版社,2003.
[4] Kent Beck,Cynthia Andres.解析極限編程:擁抱變化[M].北京:機械工業(yè)出版社,2011,20-26.
[5] Mani Chandy K., Event-Driven,Applications: Costs, Benefits and Design Approaches[J].California Institute of
[6] Scott Meyers,Effective C++:55 Specific Ways to Improve Your Programs and Designs, 3rd Edition[M].Pearson Education Inc.,2005.
[7] Jimmy Nilsson.領域驅動設計與模式實戰(zhàn)[M].北京:人民郵電出版社,2009,11.
[8] 楊芙清.軟件工程技術發(fā)展思索[J].軟件學報,2005,1.
[9] 張凱.軟件缺陷混沌分形描述與軟件質量進化度量的研究[D].武漢:武漢理工大學,2005
The Research of Improving Code Reusability
Zhang Xiaoyu
(Zhongxing Telecommunication Equipment Corporation, Shanghai 201203, China)
Abstract:Many ways to improve the profit in software company. Reducing cost is the easiest way for software developers. Improving code resuability is a kind of way to cut off cost. Developer can use the existing elements of current system with high reusability by the least cost when need comes. This paper focus on how to improve code reusability.
Key words:Software Developing; Cost Reducing; Reusability
收稿日期:(2015.05.04)
作者簡介:張曉宇(1987-),男,阜陽,中興通訊股份有限公司,通訊軟件開發(fā)工程師,研究方向:通訊軟件開發(fā)與應用,上海,201203
文章編號:1007-757X(2016)01-0048-03
中圖分類號:TP311
文獻標志碼:A