1. 程式人生 > >【連載】優秀程式設計師的 45 個習慣之習慣35

【連載】優秀程式設計師的 45 個習慣之習慣35

對問題各個擊破

——  高效程式設計師的 45 個習慣之習慣35

“逐行檢查程式碼庫中的程式碼確實很令人恐懼。但是要除錯一個明顯的錯誤,只有去檢視整個系統的程式碼,而且要全部過一遍。畢竟你不知道問題可能發生在什麼地方,這樣做是找到它的唯一方式。”
   

單元測試(在第 76 頁, 第 5 章)帶來的積極效應之一,是它會強迫形成程式碼的分層。要保證程式碼可測試,就必須把它從周邊程式碼中解脫出來。如果程式碼依賴其他模組,就應該使用 mock 物件,來將它從其他模組中分離開。這樣做不但讓程式碼更加健壯,且在發生問題時,也更容易定位來源。

否則,發生問題時有可能無從下手。也許可以先使用偵錯程式,逐行執行程式碼,並試圖隔離問題。也許在進入到感興趣的部分之前,要執行多個表單或對話方塊,這會導致更難發現問題的根源。你會發現自己陷入整個系統之中,徒然增加了壓力,而且降低了工作效率。

大型系統非常複雜 —— 在執行過程中會有很多因素起作用。從整個系統的角度來解決問題,就很難區分開,哪些細節對要定位的特定問題產生影響,而哪些細節沒有。

答案很清晰:不要試圖馬上了解系統的所有細節。要想認真除錯,就必須將有問題的元件或模組與其他程式碼庫分離開來。如果有單元測試,這個目的就已經達到了。否則,你就得開動腦筋了。

比如,在一個時間緊急的專案中(哪個專案的時間不緊急呢 Fred George 發現他們面對的是一個嚴重的資料損毀問題。要花很多精力才能知道哪裡出了問題,因為開發團隊沒有將資料庫相關的程式碼與其他的應用程式碼分離開。他們無法將問題報告給軟體廠商,當然不能把整個程式碼庫用電子郵件發給人家!

於是,他們倆開發了一個小型的原型系統,並展示了類似的症狀;然後將其傳送給廠商作為例項,並詢問他們的專家意見,使用原型幫助他們對問題理解得更清晰。

而且,如果他們 無法 在原型中再現問題的話,原型也可以告訴他們可以工作的程式碼示例,這也有助於分離和發現問題。

識別複雜問題的第一步,是將它們分離出 來。既然不可能在半空中試圖修復飛機引擎,為什麼還要試圖在整個應用中,診斷其中某個組成部分的複雜問題呢?當引擎被從飛機中取出來,而且放在工作臺上之 後,就更容易修復了。同理,如果可以隔離出發生問題的模組,也更容易修復發生問題的程式碼。

    分離原型                          Prototype to isolate

可是,很多應用的程式碼在編寫時沒有注意到這一點,使得分離變得特別困難。應用的各個構成部分之間會彼此糾結:想把這個部分單獨拿出來,其他的會緊隨而至。 在這些狀況下,最好花一些時間把關注的程式碼提取出來,而且建立一個可讓其工作的測試環境。

對問題各個擊破,這樣做有很多好處:通過將問題與應用其他部分隔離開,可以將關注點直接放在與問題相關的議題上;可以通過多種改變,來接近問題發生的核心 —你不可能針對正在執行的系統來這樣做。可以更快地發現問題的根源所在,因為只與所需最小數量的相關程式碼發生關係。

隔離問題不應該只在交付軟體之後才著手。在構建系統原型、除錯和測試時,各個擊破的戰略都可以起到幫助作用。

對問題各個擊破 在解決問題時,要將問題域與其周邊隔離開,特別是在大型應用中。

切身感受

面對必須要隔離的問題時,感覺就像在一個茶杯中尋找一根針,而不是大海撈針。

平衡的藝術

  • 如果將程式碼從其執行環境中分離後,問題消失不見了,這有助於隔離問題。
  • 另一方面,如果將程式碼從其執行環境中分離後,問題 還在 ,這也有助於隔離問題。
  • 二分查詢 的方式來定位問題是很有用的。也就是說,將問題空間分為兩半,看看哪一半包含問題。再將包含問題的一半進行二分,並不斷重複這個過程。
  • 在向問題發起攻擊之前,先查詢你的解決問題日誌(見第 129 頁上的習慣 33 )。