l

2019年2月26日 星期二

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

Feb. 25 18:20~19:38

▲人神之間的共通語言XD


Tennis Refactoring Kata介紹

鄉民們可以在這裡找到10幾個語言的Tennis Refactoring Kata程式碼。以Java語言為例,專案結構如下,一共有五個Java檔案:


  • TennisGame:定義TennisGame介面,讓TennisGame1~TennisGame3實作。


  • TennisGame1:第一個版本的重構對象,裡面有幾個很明顯的壞味道(Bad Smell),也有幾個小地方稍微把邏輯整理一下就可以完成重構任務。


  • TennisGame2:第二個版本的重構對象,這個版本的程式碼更長,用最無腦的方式把計分規則逐一展開,想要模擬不太會寫程式的新手所完成的作品。乍看之下有點頭大。但只要做過第一個版本,你已經知道網球遊戲的計分規則。只要把成對的if逐一合併之後,重構就大致完成。


  • TennisGame3:第三個版本的重構對象,這個版本的程式最精簡,只要套用rename與extract method這兩個重構就差不多可以搞定。


  • TennisTest:測試案例,因為這個kata的目的是要練習refactoring,所以範例程式提供了完整的測試案例。鄉民們在練習的時候,只要測試案例通過,就代表沒有改變程式原本的外部行為。


Teddy今天不是要討論如何重構這三個class,重構的活動就交給鄉民們自行練習。

***

和DDD有什麼關係?

▼下列程式為TennisGame2的(一種)重構結果。


鄉民們可以發現,經過重構後計算分數的邏輯分成四個階段:

  • BeforeDeuce:在deuce之前。
  • Deuce:發生Deuce的時候。
  • Advantage:差一分就獲勝的時候。
  • GameOver:這一局結束。

上述程式當然還可以進一步重構,眼尖的鄉民也許發現可以套用State設計模式,把getScore函數簡化成只有一行:

public String getScore() {
         return state.getScore();
     }

這整個過程是一小步、一小步重構之後,如果幸運的話最後走到套用State設計模式,讓自己看起來好棒棒。但是,鄉民們也可以從領域驅動(DDD)設計的角度來思考這個重構。假設鄉民們找到網球專家跟你一起討論需求,從網球專家的互動中,你們整理出以下domain model:

此時你們只關注Game與Game的計分方式。因為Game的計分方式與狀態有關,不同的狀態有不同的分數顯示邏輯。在與領域專家討論時,你們決定把Deuce之前的狀態稱為Initial,其他三個計分狀態分別為Deuce、Advantage、GameOver。

到這邊為止,有兩種可能的方式進行重構:

  • 小步快跑:繼續採用小步快跑的方式,慢慢重構系統,不要直接決定就是要套用State設計模式,除非程式的結構已經很明顯表達出這種可能性。
  • 重構為模式:與領域專家討論過後,你對於問題領域更佳清楚。domain model已經暗示了可以套用State設計模式,於是你決定「用跳的」,直接把程式重構為套用State設計模式。

以下為套用State設計模式後的檔案


套用State設計模式的TennisGame。Teddy把state切換邏輯寫在TennisGame身上,也可以改成放在每一個具體狀態類別身上。

***

結論

用最初淺的方式來看DDD,花點時間整理domain model,並建立ubiquitous language。無論採用小步快跑的重構,直接重構成設計模式,或是採用TDD的方式來開發系統,有一個共同的語言,對於任何方式的軟體開發都很有幫助

***

友藏內心獨白:寫程式有時候不是英文不好不會取名字,而是沒有花時間建立ubiquitous language。

沒有留言:

張貼留言