🎯 什麼情境該想到我

當你「要加的功能該放在某個類別上,但那個類別是會把整個世界都吸進測試用具的『黑洞』類別」的時候。

⚙️ 怎麼用(步驟)

書先講一般原則:修改一個類別的最佳途徑,就是在測試用具中建立它的實例,為你想要進行的修改編寫相應的測試,然後作出修改來滿足該測試。然而有時候為了將一個類別納入測試需要花的工夫太大了(p.302)。作者舉的實例:某團隊接手的遺留系統裡,那些領域相關的類別幾乎直接依賴了系統內的其他所有類別;更糟的是這些類別居然還統統被綁進了一個持久化框架。把裡面的一個類別納入測試框架仍然是可行的,但如果時間都花在跟那些領域類別糾纏上面,做正事的時間就被佔用掉了。在那種情況下,為了獲得一些必要的分離,就用上了本節的技術(p.302)。

書中的例子是一個音樂合成工具:需要判斷一段音樂模式能否放進一條音軌的某個序列(找出序列中的「死時間」)。理想情況下該方法應該放在序列類別上,但序列類別正是那種黑洞類別;而事件類別的依賴情況並不比序列類別好,都會給建置帶來麻煩。關鍵轉折是:要計算一段模式能否放進一個序列,其實只需要每個事件的持續時間——於是可以先寫一個「基於整數來進行計算」的自由函式(不屬於任何類別,操作的是基於基本型別的序列表示),為它寫測試;有了它之後,再在原類別上加一個非常簡單的方法,把實質性的工作全部委託給那個自由函式(p.302–303)。

書中列的步驟(p.304):

  1. 編寫一個自由函式來實現你想要對目標類別做的事情。同時建立一個中間表示,以便你的自由函式進行處理。
  2. 往目標類別上添加一個函式來建構這一中間表示,並將實際任務轉發給上一步建立的那個自由函式。

🧪 我實際套用的紀錄

  • (待填)

⚠️ 注意 / 什麼時候不適用

書對這一手法的評價明顯偏負面,而且是作者自己在示範完之後直接承認的:「到目前為止,已經可以實現我們想要的特性了,但做法是非常醜陋的。」(p.303)書列出五個問題(p.303):

  1. 暴露了原類別的內部表示。
  2. 令原類別的實作更難理解,因為我們將其實作的一部分推到了一個自由函式中。
  3. 寫了一些沒有測試覆蓋的程式碼(實際上我們是沒法給那個取出持續時間陣列的函式編寫測試)。
  4. 重複資料。
  5. 拖延了問題。我們並未解開領域類別與基礎架構之間的依賴(這一點會給後面的工作帶來很大的影響)。

其他限制:

  • 「儘管有這許多缺點,我們終究還是得以把一個有測試覆蓋的特性加進去了。我並不喜歡這類重構,但如果已經沒有其他選擇的話,也只能這麼辦了。」(p.304)
  • 「樸素化參數手法對程式碼的狀況並無多大改善。總的來說,比較好的辦法是將新程式碼加到原類別上,或使用新生類手法來建立新抽象,充當後續工作的基礎。我使用樸素化參數手法的唯一一次,是當我覺得有信心在後面能騰出時間來把我的類別納入測試時;到那時候,便可以把我的方法放到這個類別上了。」(p.304)
  • 通常它是新生類(54 頁)手法的一個不錯的準備——你可以設想把那個自由函式包覆在一個新類別中的情形(p.304)。
  • 與《重構》的分界:這根本不是在改善結構——書自己說它「對程式碼的狀況並無多大改善」,還會暴露內部表示、複製資料。它存在的唯一理由是:在完全打不進測試的黑洞類別上,仍然把一個有測試覆蓋的新特性加進去。用它等於欠下一筆技術債,並且承諾之後回來還。

🔗 相關工具