🎯 什麼情境該想到我
當你「面對的是 C 這種程序式語言,沒有共同介面也沒有共同基底類別可以拿來換掉那個函式」的時候。
⚙️ 怎麼用(步驟)
書中先講清楚背景:物件導向方法學給了我們很多替換物件的契機,只要兩個類別實作了共同的介面、或具有共同的基底類別,就能輕鬆把其中一個的物件替換成另一個的物件;但像 C 這樣的程序式語言的程式設計師「沒這個福氣」——若不用預處理手段,就完全沒法在編譯期把一個函式替換成另一個函式(p.296–297)。
連接替換的做法:建立一個啞元庫,該庫裡面包含一個跟原函式簽名完全一樣的函式。如果你的目的是感知,那麼需要在該函式裡面設置一些機制來保存通知訊息並對它們進行查找——可以使用檔案、全域變數,或其他辦法,只要你覺得方便就行(p.297)。書中的示例是在測試用的函式裡建立一個全域清單,把每次該函式被呼叫時的有關資訊記下來;測試時就可以在測完一組物件之後檢查該清單,確認該函式是否按照正確的順序被呼叫了(p.297)。
在 Java 中也可以用這一手法:建立一個同名同方法集的類別,然後修改類別路徑,讓呼叫被決議到你的新類別上,從而避開原類別上的糟糕依賴(p.297)。
書中列的步驟(p.297):
- 找出你想要偽造的函式或類別。
- 為它們編寫另一份定義。
- 修改你的建置參數,讓你的偽造品能夠代替真品被連接到專案中。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 殺雞用牛刀:書在後文談程序式語言的解依賴選項時明說,連接替換(296 頁)與定義補全(266 頁)雖然可用,但「對於解開一些小的依賴又顯得有點殺雞用牛刀了」(p.310)。小依賴不要動用它。
- C++ 上作者沒實證:作者自陳「我個人從未對 C++ 類別用過這一手法,但我想道理是一樣的」,並認為 C++ 編譯器的名字粉碎機制肯定會帶來一些困難;但如果呼叫的是 C 函式,則該手法是非常可行的(p.297)。
- 最適合的場景很窄:其最大的用處就是用來偽造外部函式庫內的函式;而最好偽造的又屬那些「純資料匯」的函式庫——對於這類函式庫,你只是呼叫其中的函式,通常並不關心其回傳值,圖形函式庫就是這樣的一個例子(p.297;「匯」原文譯註亦稱「宿」「池」)。
- 與《重構》的分界:這不是在改善設計結構,而是在建置/連接階段動手腳,只為了讓原本無法替換的呼叫在測試中被換掉。它甚至沒有碰到產品原始碼一行——動機是「讓程式能進測試」,不是「讓程式更好讀」。
🔗 相關工具
- 工具-打破依賴以便測試 — 這條技術屬於的大類
- 參數化建構函式、參數化方法 — 物件導向語言裡比較常用的替換手段;連接替換是這些路都走不通時的備案
- 定義補全 — 書在 p.310 把它與連接替換並列為程序式語言可用的手法,並同樣提醒對小依賴是殺雞用牛刀
- 回連 修改程式碼的藝術