🎯 什麼情境該想到我

當你「用的是直譯語言,只想讓某一個方法在測試中不要真的被執行」的時候。

書上的定位:「一些新的直譯語言提供了非常好的解依賴途徑。在被直譯的時候,方法可以被及時重定義。」(p.323)

書中的 Ruby 例子:Account 有個 deposit 會呼叫 report_deposit。「如果我們不想看到 report_deposit 在測試中被執行,則只需在測試檔案中重定義,並將測試程式碼放在新的定義之後。」(p.323)

關鍵在於「只重定義方法、不重定義類別」: 「有一點非常值得注意,即我們並沒有重定義整個的 Account 類別,而只是重定義了它的 report_deposit 方法。Ruby 直譯器將 ruby 程式碼檔案的每行都看作可執行語句。而 class Account 語句就相當於打開了 Account 類別的定義,從而新的方法定義可以被添加進去。而 def report_deposit(value) 語句則開始往打開的類別中添加新方法。Ruby 直譯器並不關心該方法是否已有定義體存在,如果有的話就替換掉。」(p.323–324)

(簡體版譯者在此加註:這個手法一般叫「文本替換」。)

⚙️ 怎麼用(步驟)

書中列出的步驟——標題就寫明「在 Ruby 中使用文本重定義的步驟」(p.324):

  1. 找出你想要替換定義的方法所在的類別。
  2. 在測試原始檔開頭添加一行 require,引入包含目標類別的模組。
  3. 在測試原始檔的開頭給每一個你想要替換的方法提供新的定義。

順序很重要:書中示範的檔案結構是 require → 重新打開類別、重定義方法 → 然後才是測試程式碼,並明講要「將測試程式碼放在新的定義之後」(p.323)。

🧪 我實際套用的紀錄

  • (待填)

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

  • ⚠️ 語言限制:書中示範與步驟都限定在直譯語言,具體例子是 Ruby。(p.323–324)它靠的是「程式碼檔案每一行都是可執行語句、類別定義可以被重新打開」這個語言性質。

  • ⚠️ 主要缺點:重定義是全域且不可逆的。 書中的側欄(p.324):

    「Ruby 中的文本重定義也有它的缺點:新方法對舊方法的替換會一直有效,直到程式結束。所以如果你後面忘記了某方法已被重定義了的話就可能會遇到麻煩。」

    換句話說,你在測試檔開頭做的這件事會污染整個測試程序的其餘部分——這是拿「完全不用改產品程式碼」換來的代價。

  • C/C++ 也有對應做法,但走的是另一條路。 「其實我們在 C/C++ 中也可以進行文本重定義——使用預處理。關於這個的一個例子可以參考第 4 章描述預處理期接縫(29 頁)一節中的例子。」(p.324)

🔗 相關工具