顯示具有 Refactoring 標籤的文章。 顯示所有文章
顯示具有 Refactoring 標籤的文章。 顯示所有文章

2009年11月8日 星期日

我學 TDD 的方式

TDD ( 測試驅動開發 ) 的動機請見《為什麼要寫 unit test?為什麼要先寫測試?》。這篇簡記一下我學習 TDD 的方式:

看完書後,大致上是從大師的 Blog 跳著看有興趣(或著說,讀起來比較有感覺)的部份。自己寫個一陣子就看個幾篇,待思考消化後再回頭試著用自己的方式寫出來。如此反覆進行。最重要的是在自己的環境裡,用自己的方式進行 TDD,感受會更明確。

TDD 的概念很簡單,看過例子後應該能馬上使用,但無法立即精通 TDD 。它是一個習慣,而培養習慣需要時間累積。養成習慣後,接著需要提昇測試和重構的技巧,才能逐步提昇效果並降低使用 TDD 的成本。如同過去學 Design Pattern 或其它寫程式的技巧一般,需要讀書和時間練習。我在前幾次使用 TDD 時有犯一些錯,使得效果打折,有感受到一些好處,但也對一些壞處感到疑惑。直到寫了
五、六個小專案後,才釐清一些疑慮,確信 TDD 是很划算的取捨,從而決定持續使用 TDD。最近用它寫超過一萬行程式,更加感受到它的威力。

備註

  1. Java 的例子可見 The Bowling Game Kata;Python 的例子可見 Dive into Python 3 介紹 unittest module 的章節介紹 refactoring 的章節。《Dive into Python 3》的例子比較不像真實的 TDD,為了教學方便,作者直接寫最終版的測試碼。若要看原汁原味的演化過程,看 Java 的例子或 Kent Beck 的書較適當。

2009年4月19日 星期日

最近用 Python + TDD 心得(與 Java 做對照)

最近又用 Python 寫了些程式,剛好和之前用 Java 進行 TDD 做個對照。

若不知道 TDD 的人,可以先參考這篇,TDD 的概念是依以下三個步驟寫程式 ( 順序相當重要 ):

  1. 先寫簡單的單元測試,並執行它。
  2. 用最簡單的方法實作需要的功能,讓程式能通過測試。
  3. 重構程式,並確保重構後的程式仍能通過測試。

實作部份不需多談,這裡先分別對步驟一「寫測試碼」和步驟三「重構」討論,再分享一般性的一點心得。

寫測試碼

Python 寫起測試碼比 Java 簡單許多,可以輕易地抽換所有既有物件,如 module、function、class、method。下面是一個簡單的抽換 method 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass(object):
    def hello(self, msg):
        print msg
 
def new_hello(self, msg):
    print msg + " (by replaced method)"
 
obj = MyClass()
obj.hello('Testing rocks!')  # Testing rocks!
mystub = stub.Stub()
mystub.replace(MyClass, 'hello', new_hello)
obj.hello('Testing rocks!')  # Testing rocks! (by replaced method)
mystub.restore(MyClass, 'hello')
obj.hello('Testing rocks!')  # Testing rocks!

註解表示該行輸出結果,其中 Stub 是我寫得一個簡單 class,只有十行多,可以替換和還原物件。可以從 MyClass.hello() 的輸出看出,stub.replace() 後 MyClass.hello() 被換為 new_hello(),stub.restore() 後則換了回來。完整的程式碼請看這裡

方便抽換物件有什麼好處呢?舉例來說,若 class A 有兩個 method B 和 C,其中 B 用到 C ( B 會視 C 傳回結果改變程式流程 )。為了方便測試 B 的行為,得先控制 C 的傳回結果。若是 Python 的話,照上面的例子透過 Stub ( setattr() ) 替換 A.C 即可。但在 Java 的情況,除非將 C 當作參數傳給 B,讓測試程式有機會傳「假的 C 」給 B,不然難以控制 B 內部行為。寫新程式時還有機會改設計,對於舊的程式,修改程式是個災難。用到別人的函式庫或沒程式碼就無解了。不論 Java 有何解法 ( 如採用 injection 的方式 ),解法愈麻煩,表示大眾愈不願意做,因此降低測試碼的品質,連帶影響 TDD 效果。

Mock library 部份,Java 有 EasyMock,Python 有 Mox,兩者用法差不多。後者是前者改來的,學一套兩邊都可以用。

重構

由於 Java 的特性 ( static type ),重構工具相當成熟,我慣用 Eclipse 的重構功能,離開它就不太想寫 Java。其中我最常用的功能是改名稱,包含 variable、method 、 class、 package 等。好的名稱是好程式碼的必要條件之一,好的名稱可以省去冗長的註解、縮短理解程式的時間,同時也有助於作者釐清邏輯,減少犯錯的機會。若發現名稱很難取,也許表示該 object(method / class / package)功能沒規範好,之後容易遇到問題。寫程式難免會犯錯,寫一段時間後才發現命名不夠精確,得回頭修改。若有好的重構工具,改起來快又沒風險,可以提高重構的意願。

反觀 Python 因為 dynamic type,程式碼本身提供的訊息不足(得執行後才清楚全貌),難以透過工具重構。我找了一下相關工具後沒看到滿意的。Eclipse 的 PyDev plugin 有提供重構,但試用後結果是錯的,改 module 名稱時沒動到檔名,執行後才會發現程式碼爛了。另外試了 Rope ,可以正確改名,可是速度有點慢,操作相當不直覺 ( 之後再來試看看 ropevim )。為了長遠發展考量,或許可以試看看自己弄個簡單版的改名工具或改 Rope,工具的功能可以少,但要快、容易操作且結果正確。

結語

只有在三個步驟都確實做到時,TDD 才能發揮應有的威力。依我目前一點點的實作心得,Python 容易寫測試碼、卻不方便重構;而 Java 正好相反。現在我大多有照 TDD 的流程寫程式,即使寫個一小時的小程式,也會用 TDD 。有時寫寫覺得卡卡的,才發現忘了先寫測試碼。

另外,測試碼的範圍抓得準 ( 別測太細,也別懶得測核心) ,效果才會好。我初用 TDD + Python 時就矯枉過正,寫太多測試變成 「over-testing」,省了 over-design 的負擔,卻多花時間寫不必要的測試碼、又增加日後維護的成本 。

養成 TDD 習慣的關鍵,在於寫測試碼的功力,像是如何準備 fixture ,如何改善函式介面以利測試。為了寫測試而改變函式介面並不是本末倒置,通常這會降低函式之間的關聯性,將功能明確切開,讓每個函式的輸入輸出都很乾淨(有簡單的輸入,才方便準備 fixture)。而乾淨的輸入輸出意味著函式更容易被組合使用。

2009年3月15日 星期日

軟體開發技巧的待讀書單

經過這陣子的評估,終於列出了夢幻般 (?) 的讀書清單。我的做法是先在網上看到有人推薦,到書局翻一陣子,再回來查 Amazon 的評論,最後決定是否有必要看。附帶一提,待這書單決定後又查了一下,結果發現每本書都有得到 Jolt Award,一瞬間好像以為 Jolt Award 不值錢了 :-)。

以下依暫定的閱讀順序依序說明,除前兩本外,後三本都有中文版:

1. Test Driven Development by Examples

Amazon 評論 4 顆星,由 Kent Beck 所著。全書只有 200+ 頁,用大量的例子,一步一步說明怎麼進行 TDD,相當易讀。這裡有別人寫的詳細書評

2. xUnit Test Patterns: Refactoring Test Code

Amazon 評論 4.5 顆星,不過只有七筆評論,樣本略嫌不足。 在書店試翻的感想是:好書但不易讀,而且實在是太厚了。ThoughtWorks 專出這種書嗎?

3. Refactoring to Patterns

Amazon 評論 4 顆星,但評論兩極化落在 3 和 5,最中肯的評論為: "Good ideas, but needs refactoring",值得一讀,但不好消化。讀過 Martin Fowler 的 Refactoring 並有一段實戰經驗後的最佳書藉。本書中文版《重構-向範式前進》,且是侯捷合譯的 。

4. Head First - Head First Object-Oriented Analysis and Design

Amazon 評論 4 顆星,評論裡指出本書適合初學者,另外最多人同意的評論 (Decent Introduction to OOA&D) 指出:本書不夠簡潔,並有不少小錯,若有第二版才值得推薦。 

5. Head First - Design Pattern

Amazon 評論 4.5 顆星,且是壓倒性的一堆 5 顆星。Head First 的書以易讀出名,但通常也寫得很厚,需要花不少時間消化 。 對照過於精簡典雅的 Design Pattern Bible Book,這本書親切不少。附帶一提,在書店還有看到 Head First - Algebra,真是太有趣了!可惜是教國中代數,不是教線性代數。

依目前心得來說,有四件事要學:

  1. 寫出良好的測試程式 - book 2
  2. 寫出良好的 OOP- book 4, 5
  3. 提昇重構技巧 - book 3
  4. TDD (其實就是上述三者的綜合體) - book 1

前往軟體開發聖殿之路是很遠的,希望一個月至少能解決一本,並持續地應用到實戰中,半年後就出師啦!

備註

這裡補充我曾看過且大力推薦的書藉: