🎯 什麼情境該想到我
當你「想把一個類別納入測試,卻發現建構函式裡把物件建立寫死了,測試時既換不掉也不想建」的時候。
書中 p.276 講得很直白:「在你試圖將一個類納入測試時,可能會發現建構函式中的物件建立令你頭疼不已。有時候你並不想在測試的時候建立這些物件。還有些時候你或許只是想放置一個感知物件。然而現實是你沒法做到這一點,因為這些物件的建立被固定在建構函式體中了。」同頁邊註一句話總結:「建構函式中固定了的初始化工作可能會給測試帶來很大的麻煩。」
p.277 的例子裡,某個類別在建構函式中直接建立了一個交易管理員物件。書中說:「如果這個物件是在其他地方被建立的話,我們便能更容易地引入分離。要實現這個條件,選擇之一便是使用提取並重寫工廠方法。」
⚙️ 怎麼用(步驟)
書中 p.278「步驟」小節:
- 找出建構函式中的新建物件處。
- 將所有涉及新建該物件的程式碼通通轉移到一個工廠方法中。
- 建立一個測試子類,重寫剛才的那個工廠方法以避免測試期間對問題類型的依賴。
p.277 對第 3 步的說明:「有了工廠方法之後,便可以將它子類化並重寫了,我們可以在重寫的工廠方法中返回我們想要返回的東西。」
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 在 C++ 完全行不通。 p.277 邊註:「提取並重寫工廠方法是個相當強大的手法,但它存在一些語言相關的問題。例如它在 C++ 裡面是行不通的。C++ 並不允許在建構函式中的虛擬函式呼叫被決議到衍生類別中去。而 Java 及許多其他語言則允許這一點。」
- C++ 的替代路線:同段接著說「那麼在 C++ 中怎麼辦呢?可以考慮用替換實例變數(書 p.317)和提取並重寫獲取方法(書 p.278)來代替」,並指出替換實例變數一節有示例及討論。
- 這招的代價是:為了測試而在正式類別上開一個 protected 的工廠方法,並在測試端多養一個子類別。書中把這視為為了取得分離而付出的必要成本——p.278 步驟第 3 步明講建立測試子類的目的就是「以避免測試期間對問題類型的依賴」。