🎯 什麼情境該想到我

當你「想改的邏輯跟一堆礙事的依賴(例如 UI 相關的函式與類別)攪在同一個類別裡,光是把它建起來就辦不到」的時候。

⚙️ 怎麼用(步驟)

書先界定適用邊界:有些類別裡面的問題依賴並不多;如果這些依賴被包含在少數幾個方法中的話,你可以採用子類化並重寫(314 頁)手法來將它們解決掉。但如果依賴猖獗,則這條路可能就行不通了,這時你可能就需要動用介面提取技術,重複運用介面提取來解除對某些特定型別的依賴。依賴下推則是另一個選擇——該手法能夠將目標類別其他部分的問題依賴分離出來,使你能夠更容易地在測試用具中將它實例化(p.307)。

做法概述(p.308):在使用依賴下推技術時,**首先把目標類別設為抽象類別,然後建立一個它的子類別,後者便是你新的產品類別了。接著將所有的問題依賴都「下推」到這個子類別中。**到這一步,你便可以透過子類化原類別來讓它的有關方法接受測試了。

書中的例子是一個 C++ 的交易驗證類別:想修改它裡面的驗證邏輯就會遇到麻煩,因為「我們可不希望將 UI 相關的函式和類別牽扯到測試用具中來」(p.308)。把 UI 相關的工作下推到一個新的子類別之後,就可以再建立另一個測試用的子類別,後者只需實作一個空的顯示訊息方法即可(p.309)。

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

  1. 在測試用具中建構目標類別。
  2. 找出哪些依賴是導致建構問題的依賴。
  3. 建立目標類別的子類別,子類別的名字須反映上一步找出的依賴的特徵。
  4. 將目標類別中的依賴變數和方法全部複製到新建的子類別中,注意保持簽名;將目標類別中的相應方法設為受保護及抽象的;將目標類別設為抽象的。
  5. 建立目標類別的一個測試子類別,修改你的測試,實例化該測試子類別。
  6. 建立測試來驗證你的確能實例化這個新的測試子類別。

🧪 我實際套用的紀錄

  • (待填)

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

書在示範完之後給了明確的自我評價(p.310):

  • 「如此一來我們便有了一個可測試但同時又不依賴於任何 UI 相關事物的類別。那麼,在這個例子中使用繼承是否為理想的方案呢?不是的,但它最大的好處就是能幫助我們將目標類別的一部分邏輯納入測試。」
  • 它是起點而非終點:一旦有了對該類別的測試,便可以開始清理顯示訊息方法裡面的重試邏輯,並將其從那個 UI 子類別提升到基底類別中來;然後,當該 UI 子類別最終被掏空得只剩 UI 相關的呼叫時,我們便可以轉向委託式的設計了——即將 UI 相關呼叫委託給一個 UI 依賴的新類別(p.310)。
  • 不是萬用起手式:依賴少而集中時,書要你先用子類化並重寫(314 頁);依賴猖獗到某些特定型別上時,也可以重複運用介面提取。依賴下推是這兩條路之外的另一個選擇(p.307)。
  • 步驟裡藏著保命細節:搬動時要保持簽名(p.310 步驟 4),且第 6 步要求另外寫一個測試來驗證「你的確能實例化這個新的測試子類別」——這一步本身就是這個手法的成敗判準。
  • 與《重構》的分界:外觀上像在整理繼承體系,但動機是把礙事的依賴掃進一個新子類別,好讓抽象基底類別能在測試用具裡被實例化。書明說用繼承在這裡並非理想方案,只是它能換到「把一部分邏輯納入測試」;設計上的正解(委託)要等測試就位以後才做。

🔗 相關工具