您把多少時間花在維護項目構建腳本上?也許遠遠超出您預期的或者可以忍受的時間。其實大可不必遭受如此痛苦的經歷。在這一期的讓開發自動化 中,開發自動化專家 Paul Duvall 將演示如何改進很多常見的妨礙團隊創建一致的、可重復的、可維護的構建的實踐。
當描述代碼之類的東西時,我不喜歡 “氣味(smell)”這個詞。因為用擬人的手法來談論比特和字節往往令人覺得很怪異。并不是說“氣味”這個詞不能準確地反映出某種表明代碼可能有錯誤的癥狀,只是我覺得這樣聽起來很滑稽。然而,我依然選擇再次用這種令人厭煩的方式來描述軟件構建,坦白說,這是因為這些年我見過的很多構建腳本都散發著難聞的氣味。
在創建構建腳本時,即使是偉大的程序員也常常會遇到困難。就好像最近才學會如何編寫程序性 代碼似的 —— 他們還會編寫龐大的單塊構建文件、通過復制-粘貼編寫代碼、對屬性進行硬編碼等等。我總是很想知道為什么會這樣。也許是因為構建腳本沒有被編譯成客戶最終會使用的東西?然而我們都知道,要創建客戶最終使用的代碼,構建腳本是中心,如果那些腳本敗絮其中,那么要想有效地創建 軟件,就需要克服重重挑戰。
幸運的是,您可以輕松地在構建(不管是 Ant、Maven 還是定制的)之上部署一些實踐,它們雖然可以幫助您創建一致的、可重復的、可維護的構建,但其過程會很長。學習如何創建更好的構建腳本的一種有效的方法是搞清楚哪些事情不要 去做,理解其中的道理,然后看看做事的正確 方法。在本文中,我將詳細論述您應該避免的 9 種最常見的構建中的氣味,為什么應該避免它們,以及如何修復它們:
- 惟 IDE 的構建
- 復制-粘貼式的編寫腳本方法
- 冗長的目標
- 龐大的構建文件
- 沒有清理干凈
- 硬編碼的值
- 測試失敗還能構建成功
- 魔力機
- 格式的缺失
![]() |
|
這里無意給出完整的列表,不過這份列表的確代表了近年來我讀過的和寫過的構建腳本中,我遇到的較為常見的一些氣味。有些工具,例如 Maven,是為處理與構建有關的很多管道而設計的,它們可以幫助減輕部分氣味。但是無論使用什么工具,還是有很多問題會發生。
惟 IDE(IDE-only)的構建是指只能通過開發人員的 IDE 執行的構建,不幸的是,這似乎在構建中很常見。惟 IDE 的構建的問題是,它助長了 “在我的計算機上能運行”問題,即軟件在開發人員的環境中可以運行,但是在任何其他人的環境中就不能運行。而且,由于惟 IDE 構建自動化程度不是很高,因而為集成到持續集成(Continuous Integration)環境帶來極大的挑戰。實際上,沒有人為的干預,惟 IDE 常常無法自動化。
我們要清楚:使用 IDE 來執行構建并沒有錯,但是 IDE 不應該成為能構建軟件的惟一環境。特別是,一個完全用腳本編寫的構建,可以使開發團隊能夠使用多種 IDE,因為只存在從 IDE 到構建的依賴性,而不存在相反方向的依賴性,如圖 1 所示:
圖 1. IDE 與構建的依賴關系

惟 IDE 的構建有礙自動化,清除的惟一方法就是創建可編寫腳本的構建。有足夠的文檔和太多的書籍可以為您提供指導(見 參考資料),而像 Maven 之類的項目也為從頭開始定義構建提供了極大的方便。不管采用何種方法,都是選擇一種構建平臺,然后盡快地讓項目成為可編寫腳本的。
![]() ![]() |
![]()
|
復制代碼是軟件項目當中一個常見的問題。實際上,甚至很多流行的開放源碼項目都存在 20% 到 30% 的復制代碼。代碼復制令軟件程序更難于維護,同理,構建腳本中的復制代碼也存在這樣的問題。例如,想象一下,假設您需要通過 Ant 的 fileset
類型引用特定的文件,如清單 1 所示:
清單 1. 復制-粘貼 Ant 腳本
<fileset dir="./brewery/src" > <include name="**/*.java"/> <exclude name="**/*.groovy"/></fileset> |
如果需要在其他地方引用這組文件,例如為了編譯、檢查或生成文檔,那么最終您可能會在多個地方使用相同的 fileset
。如果在將來某個時候,您需要對那個 fileset
做出修改(比如說排除 .groovy
文件),那么最終可能需要在多個地方做更改。顯然,這不是可維護的解決方案。然而,要除掉這股氣味其實很簡單。
如清單 2 所示,通過 Ant 的 patternset
類型可以引用一個邏輯名稱,以表示所需要的文件。那么,當需要向 fileset
添加(或排除)文件時,只需更改一次。
清單 2. 復制-粘貼 Ant 腳本
<patternset id="sources.pattern"> <include name="**/*.java"/> <exclude name="**/*.groovy"/></patternset>...<fileset dir="./brewery/src"> <patternset refid="sources.pattern"/></fileset> |
對于精通面向對象編程的人來說,這種修復方法看上去很熟悉:既定的慣例不是在不同的類中一次又一次地定義相同的邏輯,而是將那個邏輯放在一個方法中,在不同地方都可以調用這個方法。于是,這個方法成為惟一的維護點,從而可以限制錯誤級聯并可以鼓勵重用。
文章來源于領測軟件測試網 http://www.kjueaiud.com/