🎯 什麼情境該想到我

當你「發現要測的類別在自己的建構函式裡 new 出了那個難搞的物件,測試根本插不進手」的時候。

⚙️ 怎麼用(步驟)

書的核心主張:如果你用建構函式建立了一個物件,那麼通常解除對該物件依賴的最佳辦法就是將它的建立過程外部化——即在該類別外建立該物件,然後讓該類別的客戶程式碼把這個物件傳給該類別的建構函式(p.297–298)。

書中特別點出大家的心理障礙:人們之所以並不常常想到該技術,是因為他們覺得這種做法等於強迫客戶程式碼傳遞額外參數給該類別的建構函式。然而別忘了,你還可以另外再寫一個方便的建構函式,它具有跟原來的建構函式相同的簽名(p.298)。這麼一來你就既保證了測試程式碼能夠換入必要的測試用物件,又絲毫不干擾產品程式碼(p.298)。

書中列的步驟(p.300–301):

  1. 找出你想要參數化的建構函式,並將它複製一份。
  2. 給其中的一份複製增加一個參數,該參數用來傳入你想要替換的物件。將該建構函式體中的相應的物件建立語句刪掉,改為使用新增的那個參數(如果需要賦值的話,就將那個參數賦給相應的實例變數)。
  3. 如果你的語言支援委託建構函式,那麼刪掉另一份建構函式的函式體,代以對剛才那個建構函式的呼叫,別忘了呼叫的時候要 new 一個相應物件出來。如果你的語言不支援委託建構函式,則可能需要將建構函式中的共同成分提取到一個比如叫 common_init 的方法中。
    (書中譯註:委託建構函式即「從一個建構函式中呼叫另一個建構函式」的能力。)

書中把這個過程一步一步拆給你看:先把原建構函式複製一份 → 給其中一個加上要替換的那個型別的參數 → 把這個參數賦給相應的實例變數、刪掉原來的 new 運算式 → 再回到另一個建構函式,刪除其函式體,代以對剛才那個建構函式的呼叫(p.299–300)。

🧪 我實際套用的紀錄

  • (待填)

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

  • 會擴散型別依賴:書自己列了缺點——當我們往一個建構函式上添加一個新參數時,可能會導致進一步對該參數的型別的依賴;該類別的使用者可能會在產品程式碼中使用這個新的建構函式,從而增加系統內的依賴。「不過一般來說這個問題不要緊」(p.300)。
  • 作者對它評價很高:「參數化建構函式是個非常簡單的重構手法,我喜歡用它。」(p.300)——這在本章的解依賴手法中算是少見的正面評價。
  • 預設實參是捷徑,但 C++ 有代價:在支援預設參數的語言中,只需簡單地給現有建構函式的目標參數加一個預設實參即可。但在 C++ 中這麼做有一個缺點——該類別所在的標頭檔必須包含那個參數型別的標頭檔;如果不是因為這個預設實參,只需前向宣告就夠了。「也正是因為這個原因,一般我並不使用預設實參」(p.300)。
  • 與《重構》的分界:《重構》裡改動參數列是為了讓介面更表達意圖;這裡動建構函式的唯一動機是把物件的建立權從類別內部搶到外部,好在測試裡換進測試用物件。書中留下同簽名的方便建構函式,正是為了讓產品程式碼完全不受影響——這是解依賴的思路,不是設計改良的思路。

🔗 相關工具