l

2019年2月25日 星期一

從領域驅動設計看Tennis Kata(上)

從領域驅動設計看Tennis Kata(上)

Feb. 25 12:39~14:00

▲Eiffel正在修練睡夢讀書法Kata


Tennis Kata

Kata,中文翻譯成套路。在敏捷開發領域中,kata指「被設計用來練習特定技巧(特別是程式設計)的題目與參考答案。」

最近Teddy在修改【軟體重構入門實作班】與【單元測試這樣學就會了實作班】教材內容,花點了時間重新練習了幾個kata。今天想從DDD(domain-driven design;領域驅動設計)的觀點,談一個經常被練習的題目:Tennis Kata。

Tennis Kata有兩個版本,一個用來練習TDD,另一個用來練習Refactoring。透過TDD方式撰寫或直接重構已完成的網球比賽計分程式,分別學習這兩種技能。

一個完整的網球比賽分成:

  • 比賽(Match)
  • 盤(Set)
  • 局(Game)

一場比賽有若干盤,一盤有若干局。詳細說明可參考維基百科。為了簡化起見,Tennis Kata只計算局(Game)的結果。關於局的計分方式,維基百科的解釋如下:

網球每局的記分方法:從0至3分分別為「零」(love)、「十五」(fifteen)、「三十」(thirty)和「四十」(forty)。記分時,發球手的得分在前。因此「30比0」的意思是,發球手贏得2分,而接球手還未得分。

當雙方運動員都得到了3分時,一般叫「平局」(deuce而非「40比40」。在出現平局後一名球手再得一分,被稱為「占先」(advantage),而不再記分。如果在占先的情況下失去一分,就再度回到平局;如占先後再得一分,就贏得一局。

今天先談Tennis TDD Kata,明天再談重構。

***

Tennis TDD Kata

網路上有很多Tennis TDD Kata的範例,Google搜尋時Teddy找了第一筆資料來參考。

作者把用TDD完成Tennis Kata的主要過程記錄下來,但是讀到最後完成的程式,Teddy覺得下列的IsDeuce函數的邏輯好像怪怪的。


▲節錄自Day29 TDD套路經典!? Tennis Game!


如果第一個比賽選手的分數大於等於3,IsDeuce回傳true。但根據網球比賽的規則,deuce有兩個條件:

  • 雙方選手的分數都是3分,或
  • 超過3分之後,在這一局比賽結束前,如果再度平手,也稱為deuce

那為什麼這段程式會寫成判斷 _firstPlayerScore >= 3 ,連是否平手都沒判斷?

繼續往前閱讀文章,Teddy發現作者在判斷是否為deuce的時候,production code長成下面這樣。

 

▲節錄自Day29 TDD套路經典!? Tennis Game!

接著作者用extract method重構,把 _firstPlayerScore >= 3 放到IsDeuce函數,所以造成isDeuce的邏輯變成_firstPlayerScore >= 3。

***

和DDD有什麼關係?

DDD強調domain modelubiquitous language(在限定領域中開發人員與利害關係人一致性使用的語言或是術語),幫助開發人員在系統重構時找出重構目標、重新分派物件責任,最後利用重構逐步將ubiquitous language實作在程式碼中(ubiquitous language in code)。上面這個例子,測試案例雖然都通過,程式也可以動,但是並沒有完全做到ubiquitous language in code。因為deuce的定義,包含在Tennis的ubiquitous language裡面,也就是前面提到的:

  • 雙方選手的分數都是3分,或
  • 超過3分之後,在這一局比賽結束前,如果再度平手,也稱為deuce

而上面的程式範例並沒有正確表達這個邏輯。

***

重構的錯誤

就算不考慮ubiquitous language的觀點,直接把_firstPlayerScore >= 3用extract method改成IsDuece也不洽當。為什麼,再看一次作者原本的程式碼:

 

▲節錄自Day29 TDD套路經典!? Tennis Game!

這個程式碼有兩個if,結構為:

if (_firstPlayerScore == _secondPalyerScore) {

   if (_firstPlayerScore >= 3) {

return “Deuce”;

  }

}

也就是說,deuce的條件必須兩個if同時成立原本作者的程式邏輯是對的,反倒是重構之後沒注意到if (_firstPlayerScore >= 3)其實是存在另一個 if之下,直接extract method反倒讓程式碼與領域知識不一致

如果可以把程式改成下面這樣,isDeuce就符合deuce在Tennis領域的定義,應該比較落實ubiquitous language in code的精神。

***

讓程式碼說實話

程式設計師應該大多認同,程式是寫給人看的,不是寫給電腦看的。落實ubiquitous language in code,你的程式碼就會說人話,而且是實話,不是謊話。

如此一來,可以避免程式碼和領域知識不一致。程式比較容易理解,開發人員也比較敢修改程式。這也是一種擁抱改變,讓軟體變軟的方法

***

友藏內心獨白:說實話很重要。

沒有留言:

張貼留言