1. 程式人生 > >軟體設計的哲學:第十六章 修改現有程式碼

軟體設計的哲學:第十六章 修改現有程式碼

目錄

  • 16.1 保持戰略
  • 16.2 維護註釋:將註釋放在程式碼附近
  • 16.3 註釋屬於程式碼,而不是提交日誌
  • 16.4 保留註釋:避免重複
  • 16.5 維護註釋:檢查差異
  • 16.6 更高級別的註釋更容易維護

第1章描述了軟體開發是如何迭代和增量的。大型軟體系統的開發經歷了一系列的演化階段,每個階段都添加了新的功能並修改了現有的模組。這意味著系統的設計是不斷髮展的。不可能在一開始就構思出一個系統的正確設計;一個成熟系統的設計更多地取決於系統發展過程中的變化,而不是最初的概念。前幾章描述瞭如何在初始設計和實現過程中擠出複雜性;本章討論瞭如何防止複雜性隨著系統的發展而增加。

16.1 保持戰略

第3章介紹了戰術程式設計和戰略程式設計的區別:在戰術程式設計中,主要目標是讓某些東西快速工作,即使這會導致額外的複雜性;在戰略規劃中,最重要的目標是產生一個偉大的系統設計。戰術方法很快就會導致混亂的系統設計。如果你想要一個易於維護和增強的系統,那麼“工作”並不是一個足夠高的標準;你必須優先考慮設計並有策略地思考。這種思想也適用於修改現有程式碼時。

不幸的是,當開發人員深入到現有的程式碼中進行諸如bug修復或新特性之類的更改時,他們通常不會進行戰略性思考。一個典型的心態是“我能做的最小的改變是什麼來滿足我的需要?”“有時候開發人員這麼做是因為他們不喜歡修改程式碼;他們擔心更大的變化會帶來更大的引入新bug的風險。然而,這導致了戰術規劃。這些最小的更改中的每一個都引入了一些特殊的情況、依賴項或其他形式的複雜性。結果,系統設計變得更糟了,問題在系統演進的每一步中不斷累積。

如果您想要維護一個乾淨的系統設計,您必須在修改現有程式碼時採取策略方法。 理想情況下,當您完成了每個更改時,系統將具有如果您從一開始就在頭腦中進行設計時所具有的結構。為了實現這個目標,您必須抵制快速修復的誘惑。相反,考慮當前的系統設計是否仍然是最好的,根據需要的更改。如果沒有,重構系統,以得到最好的設計。通過這種方法,系統設計可以隨著每一次修改而改進。

這也是第15頁介紹的投資思維模式的一個例子:如果您投入一點額外的時間來重構和改進系統設計,您將得到一個更簡潔的系統。這將加快開發速度,並且您將收回您在重構中投入的精力。即使您的特定更改不需要重構,您也應該在程式碼中尋找可以修復的設計缺陷。無論何時修改任何程式碼,都要設法改進系統設計,至少在這個過程中改進一點。如果您沒有使設計變得更好,那麼您可能使它變得更糟。

正如在第3章中所討論的,投資心態有時與商業軟體開發的現實相沖突。如果以“正確的方式”重構系統需要3個月的時間,而快速而糟糕的修復只需要2個小時,那麼您可能不得不採取快速而糟糕的方法,特別是在您的工作時間緊迫的情況下。或者,如果重構系統會造成不相容,從而影響許多其他人員和團隊,那麼重構可能是不實際的。

儘管如此,您應該儘可能地抵制這些妥協。問問你自己:“考慮到我目前的限制,這是我所能做的最好的建立一個乾淨的系統設計嗎?”也許有一種方法可以像3個月的重構那樣簡潔,但可以在幾天內完成?或者,如果你現在無法承擔大規模重構的費用,讓你的老闆給你分配時間,讓你在當前的最後期限之後再做重構。每個開發組織都應該計劃將其全部工作的一小部分用於清理和重構,這項工作從長遠來看是值得的。

16.2 維護註釋:將註釋放在程式碼附近

當您更改現有程式碼時,很可能會使某些現有註釋失效。修改程式碼時很容易忘記更新註釋,這將導致註釋不再準確。不準確的註釋會讓讀者感到沮喪,如果註釋太多,讀者就會開始懷疑所有的註釋。幸運的是,只要有一點規則和一些指導原則,就可以在不付出巨大努力的情況下更新註釋。本節和以下各節提出了一些具體的技術。

確保註釋得到更新的最佳方法是將它們放置在它們所描述的程式碼附近,以便開發人員在更改程式碼時能夠看到它們。 註釋離相關程式碼越遠,正確更新它的可能性就越小。例如,方法介面註釋的最佳位置是在程式碼檔案中,就在方法體旁邊。對方法的任何更改都將涉及這段程式碼,因此開發人員可能會看到介面註釋並在需要時更新它們。

對於像C和c++這樣具有獨立程式碼和標頭檔案的語言,另一種替代方法是將介面註釋放在.h檔案中方法宣告的旁邊。然而,這是一個很長的路從程式碼;開發人員在修改方法主體時不會看到這些註釋,而且需要額外的工作來開啟不同的檔案並找到介面註釋來更新它們。有些人可能認為介面註釋應該放在標頭檔案中,這樣使用者就可以學習如何使用抽象,而不必檢視程式碼檔案。但是,使用者不需要讀取程式碼或標頭檔案;他們應該從Doxygen或Javadoc等工具編譯的文件中獲取資訊。此外,許多ide將提取並向用戶顯示文件,例如在鍵入方法名稱時顯示方法的文件。對於這樣的工具,文件應該放在對開發人員編寫程式碼最方便的地方。

在編寫實現註釋時,不要將整個方法的所有註釋放在方法的頂部。將它們展開,將每個註釋下推到最窄的範圍,包括註釋所引用的所有程式碼。例如,如果一個方法有三個主要的階段,不要在方法的頂部寫一個註釋來詳細描述所有的階段。相反,為每個階段編寫一個單獨的註釋,並將該註釋置於該階段的第一行程式碼之上。另一方面,在一個方法的實現頂部有一個描述整體策略的註釋也是有幫助的,就像這樣:

//  We proceed in three phases:

//  Phase 1: Find feasible candidates

//  Phase 2: Assign each candidate a score

//  Phase 3: Choose the best, and remove it

16.3 註釋屬於程式碼,而不是提交日誌

在修改程式碼時,一個常見的錯誤是在原始碼儲存庫的提交訊息中放入關於更改的詳細資訊,而不是在程式碼中記錄它。儘管將來可以通過掃描儲存庫日誌來瀏覽提交訊息,但是需要這些資訊的開發人員不太可能想到要掃描儲存庫日誌。即使他們確實掃描日誌,查詢正確的日誌訊息也會很繁瑣。

在編寫提交訊息時,問問自己將來開發人員是否需要使用這些資訊。如果是,那麼在程式碼中記錄這些資訊。提交訊息就是一個例子,它描述了一個引起程式碼更改的微妙問題。如果程式碼中沒有對此進行記錄,那麼開發人員可能稍後出現並撤消更改,而沒有意識到他們重新建立了一個錯誤。如果您也想在提交訊息中包含此資訊的副本,那很好,但最重要的是在程式碼中獲得它。這說明了將 文件放在開發人員最可能看到的地方的原則;提交日誌很少在那個地方。

16.4 保留註釋:避免重複

使註釋保持最新的第二種技術是避免重複。如果文件是重複的,開發人員就很難找到並更新所有相關的副本。相反,儘量只記錄一次每個設計決策。如果程式碼中有多個地方受到某個特定決策的影響,那麼不要在每個地方重複文件。相反,找到最明顯的地方放置文件。例如,假設有一個與變數相關的複雜行為,它會影響變數使用的幾個不同位置。您可以在變數宣告旁邊的註釋中記錄該行為。如果開發人員在理解使用該變數的程式碼時遇到困難,這是一個很自然的地方。

如果沒有一個“明顯的”地方來放置特定的文件,開發人員可以找到它,那麼建立一個designNotes檔案,如第13.7節所述。或者,選擇最好的地方,把文件放在那裡。另外,在引用中心位置的其他地方新增簡短的註釋:“檢視xyz中的註釋以瞭解下面程式碼的解釋。“如果引用因為主註釋被移動或刪除而變得過時,這種不一致性將是不言而喻的,因為開發人員不會在指定的地方找到註釋;他們可以使用修訂控制歷史記錄來查詢註釋發生了什麼,然後更新引用。相反,如果文件是重複的,並且一些副本沒有得到更新,那麼開發人員就不會知道他們使用的是陳舊的資訊。

如果資訊已經在程式之外的某個地方記錄了,不要在程式內部重複記錄;只需參考外部文件。例如,如果您編寫一個實現HTTP協議的類,那麼就不需要在程式碼中描述HTTP協議。在網上已經有很多關於這個文件的來源;只需在您的程式碼中新增一個簡短的註釋,併為其中一個源新增一個URL。另一個例子是已經在使用者手冊中記錄的特性。假設您正在編寫一個實現命令集合的程式,其中有一個方法負責實現每個命令。如果有描述這些命令的使用者手冊,就不需要在程式碼中重複這些資訊。相反,在每個命令方法的介面註釋中包含如下簡短說明:

// Implements the Foo command; see the user manual for details.

重要的是讀者可以很容易地找到所有需要的文件來理解您的程式碼,但這並不意味著您必須編寫所有的文件。

不要在另一個模組中重新記錄一個模組的設計決策。例如,不要在一個方法呼叫之前加上註釋來解釋在被呼叫的方法中發生了什麼。如果讀者想知道,他們應該檢視方法的介面註釋。好的開發工具通常會自動提供這些資訊,例如,如果您選擇了一個方法的名稱或者將滑鼠懸停在它上面,就會顯示該方法的介面註釋。儘量讓開發人員容易地找到適當的文件,但不要重複文件。

16.5 維護註釋:檢查差異

確保文件保持最新的一個好方法是,在將更改提交到修訂控制系統之前花幾分鐘時間掃描提交的所有更改,確保每個更改都正確地反映在文件中。 這些預提交掃描還將檢測其他幾個問題,如意外地在系統中留下除錯程式碼或未能修復TODO項。

16.6 更高級別的註釋更容易維護

關於維護文件的最後一個想法:如果註釋比程式碼更高階、更抽象,那麼它們更容易維護。這些註釋不反映程式碼的細節,因此它們不會受到程式碼的細微更改的影響;只有整體行為的改變才會影響這些註釋。當然,正如第13章所討論的,有些註釋確實需要詳細和精確。但是一般來說,最有用的註釋(它們不會簡單地重複程式碼)也是最容易維護