天天看點

【連載】優秀程式員的 45 個習慣之習慣35 對問題各個擊破

對問題各個擊破

——  高效程式員的 45 個習慣之習慣35

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

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

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

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

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

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

于是,他們倆開發了一個小型的原型系統,并展示了類似的症狀;然後将其發送給廠商作為執行個體,并詢問他們的專家意見,使用原型幫助他們對問題了解得更清晰。

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

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

    分離原型                          Prototype to isolate

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

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

隔離問題不應該隻在傳遞軟體之後才着手。在建構系統原型、調試和測試時,各個擊破的戰略都可以起到幫助作用。

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

切身感受

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

平衡的藝術

  • 如果将代碼從其運作環境中分離後,問題消失不見了,這有助于隔離問題。
  • 另一方面,如果将代碼從其運作環境中分離後,問題 還在 ,這也有助于隔離問題。
  • 以 二分查找 的方式來定位問題是很有用的。也就是說,将問題空間分為兩半,看看哪一半包含問題。再将包含問題的一半進行二分,并不斷重複這個過程。
  • 在向問題發起攻擊之前,先查找你的解決問題日志(見第 129 頁上的習慣 33 )。
【連載】優秀程式員的 45 個習慣之習慣35 對問題各個擊破

繼續閱讀