章節概述#
本章完成了消除 Dollar 和 Franc 子類別的最後一步。既然 times() 已在上一章統一到 Money,子類別裡只剩建構子,沒有存在的理由了。同時也清理了因舊架構而存在的冗餘測試。
刪除子類別#
Dollar 和 Franc 只剩下建構子,這不足以作為保留子類別的理由。做法是將工廠方法中對子類別的參考改為直接使用 Money:
// 原本的 Franc 工廠方法
static Money franc(int amount) {
return new Money(amount, "CHF");
}
// 原本的 Dollar 工廠方法
static Money dollar(int amount) {
return new Money(amount, "USD");
}Dollar 沒有任何其他參考了,可以直接刪除。Franc 還有一個參考留在測試中:
public void testDifferentClassEquality() {
assertTrue(new Money(10, "CHF").equals(new Franc(10, "CHF")));
}清理冗餘測試#
問題是:其他地方的測試是否已經充分覆蓋了 equality?看看現有的 equality 測試:
public void testEquality() {
assertTrue(Money.dollar(5).equals(Money.dollar(5)));
assertFalse(Money.dollar(5).equals(Money.dollar(6)));
assertTrue(Money.franc(5).equals(Money.franc(5)));
assertFalse(Money.franc(5).equals(Money.franc(6)));
assertFalse(Money.franc(5).equals(Money.dollar(5)));
}現在 Money.franc() 和 Money.dollar() 都回傳 Money 實例,第三和第四個斷言與前兩個斷言是重複的(都在測試同一個 Money.equals() 邏輯),可以簡化為:
public void testEquality() {
assertTrue(Money.dollar(5).equals(Money.dollar(5)));
assertFalse(Money.dollar(5).equals(Money.dollar(6)));
assertFalse(Money.franc(5).equals(Money.dollar(5)));
}重點:
testDifferentClassEquality這個測試是為了確保「不同類別但相同幣別」能正確比較——但如果我們正在消除子類別,一個保證「子類別存在時系統能運作」的測試就不再是助力,而是負擔。刪除它,順便刪除Franc類別。
同樣的邏輯也適用於 testFrancMultiplication():既然程式碼中已經沒有基於幣別的不同邏輯(那是雙類別時代的事了),這個測試也可以刪除而不損失任何信心。
本章小結#
- 完成了子類別的清除,將
Dollar和Franc全部刪除,只留下單一的Money類別 - 隨著程式碼結構的改變,測試也需要跟著調整——在舊架構下有意義的測試,在新架構下可能是冗餘甚至有害的
- 系統準備好進入下一個大主題:加法運算