March 06 10:42~12:14
設計問題
在「開三聯式發票」系列文章中,目前domain model有四個類別:
- Invoice
- Percentage
- InvoiceBuilder
- InvoiceCalculator
但是為什麼需要這四個類別,分這麼細會不會有點過度設計的嫌疑?
▼上禮拜四Teddy在北科上課的時候,在課堂上與同學一起現場實作這個例子,有一派學生認為只要用一個Invoice類別就夠了,Invoice類別的程式碼如下:
▼Step Definition長成這個樣子。
▼測試案例通過。
到底是用四個類別來處理開三聯式發票的問題比較好,還是用一個Invoice類別搞定就好了?
***
從Refactoring角度來分析
Martin Fowler的《Refactoring》書中所介紹的程式碼怪味道(Bad Smell)共有:
Duplicated Code、Long Method、Large Class、Long Parameter List、Divergent Change、Shotgun Surgery、Feature Envy、Data Clumps、Primitive Obsession、Switch Statements、Parallel Inheritance Hierarchies、Lazy Class、Speculative Generality、Temporary Field、Message Chains、Middle Man、Inappropriate Intimacy、Alternative Classes with Different Interfaces、Incomplete Library Class、Data Class、Refused Bequest、Comments。
鄉民們有興趣可以逐一分析Invoice類別是否有那些怪味道?
▼如果真的說要有,Invoice類別有一個建構函數擁有四個參數,勉強算Long Parameter List,但也還好,這個怪味道還不算很重。
「鼻子」好一點的鄉民可能會聞到另一個Divergent Change怪味道,這是類別違反了Single Responsibility Principle(SRP,單一責任原則)的結果。Invoice類別至少負擔了三個責任:
- 計算發票
- 產生新發票
- 表達發票資料
如果把這三種責任拆開來,就跑出InvoiceCalculator與InvoiceBuilder這兩個類別。
***
拆開就沒事嗎?
把Invoice類別變成Invoice、InvoiceCalculator與InvoiceBuilder難道就比較好嗎?有人可能會說:
- 拆開後Invoice類別變成只有資料沒有什麼行為邏輯的Data Class,這也是一種怪味道啊!
- InvoiceCalculator身上都是static method,變成一個沒有狀態只提供服務的類別,這樣有符合物件導向設計嗎?
建築師Christopher Alexander說:「設計就是決定Form和Context的界線」,不管用傳統的OOAD還是透過BDD/TDD的方式,都不會保證切出一個「完美的domain model」,切得好不好有賴於開發人員對於物件導向技術與軟體設計原則與架構的理解程度。能力好的人,無論是用OOAD的方式抑或是BDD/TDD,最終都可以建構出比較理想的domain model,但兩者之間有一個很大的差別,就是Teddy在〈BDD(8):實作第一個開發票Scenario〉提到的「大批量生產」與「小批量生產」的差別。從敏捷與精實開發的角度來看,小批量生產更可以減少不必要的浪費,縮短交期(lead time)以便快速獲得回饋。
***
結論
「開立三聯式發票」功能無論是使用一個Invoice類別,或是拆成Invoice、InvoiceCalculator與InvoiceBuilder,程式都可以動。但從設計的角度來看,兩者還是有不同程度的合適性(fitness)。有興趣的鄉民們可以想想看,只有一個Invoice類別會不會造成程式碼比較不容易理解?驗證的方法可以透過寫幾個單元測試來暴露出只有一個Invoice類別會造成什麼問題。
***
友藏內心獨白:寫成一個是自然,拆成多個則是特意。
沒有留言:
張貼留言