🎯 什麼情境該想到我

當你「想測的方法沒用到任何實例變數或其他方法,但它所在的類別在測試用具裡實例化不出來」的時候。

⚙️ 怎麼用(步驟)

原理(p.273):「對付那些沒法在測試用具中實例化的類別是件麻煩事……假設你有一個方法,該方法不使用實例變數或其他方法,就可以將它設成靜態的。而一旦它成了靜態的,你便無需實例化其類別就可以將它置於測試之下了。」

為什麼不乾脆把方法搬到別的類別去(p.273):書中的例子裡,把 validate 整個移到 packet 類別上「倒的確是個不錯的主意,但就目前來說這麼做的風險還是大了點,比如首先我們就沒法實施簽名保持(249頁)。所以如果你沒有方法轉移的自動化支援的話,通常最好還是先把測試安置到位再說。……一旦測試到位,就可以放心做所需的改動,並大膽改進程式碼了」。

步驟(p.275)

  1. 編寫一個測試,存取你打算設為公有靜態的那個方法。
  2. 將目標方法的方法體提取到一個靜態方法中。記住實施簽名保持(249頁)。給這個方法起一個新的名字,看一看它的參數名,或許會有所啟發。例如一個名叫 validate 的方法接受一個 packet 參數,那麼就可以提取出一個叫做 validatePacket 的靜態方法。
  3. 編譯。
  4. 如果收到關於存取實例變數或方法的編譯錯誤,看一下那些被存取到的變數或方法,看它們能否也能被設為靜態的。如果可以,就將它們也一併設為靜態的並通過編譯。

書中的補充(p.274)

  • 在某些語言中還可以更簡單——直接把原來的方法設為靜態的即可;如果該方法被其他類別用到了,那些地方仍然可以工作,也就是說可以通過實例來呼叫靜態方法。「不過在有些語言中這麼做會招來編譯警告。而如果沒有編譯警告當然是最好不過的了。」
  • 「靜態方法不會存取類別的任何私有資料,它只是一個實用方法。如果把它設成公有的,就可以編寫測試了。之後如果你想要將該方法轉移到另一個類別中去,這些測試就會是你的強大後盾。」
  • 判斷訊號:如果你看到某個方法沒有使用任何實例資料,把它設成靜態的是個好主意,這樣可以讓它變得醒目,直到你弄清它應該屬於哪個類別。

🧪 我實際套用的紀錄

  • (待填)

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

  • 只適用於「不用實例變數也不用其他方法」的方法:這是書中給的前提條件(p.273);如果方法規模較大或用到實例資料,改用 分解出方法對象(p.261)。
  • 它會讓原本私有/實例的東西變成公有靜態:書中承認「很可能當初 RSCWorkflow 的建立者根本沒有想到會有這麼一天,它的 validate 方法被做成靜態的,更不用說公有了」,並回應:「封裝對於類別來說固然是件好事,但類別的靜態部分其實並不屬於該類別。實際上,在某些語言中,它隸屬於另一個類別,有時候也叫做元類。」(p.273–274)
  • 怕別人濫用這個入口:「如果你擔心之後又會有人來使用這個靜態方法從而帶來依賴問題,可以考慮使用非公有的存取限制。比如在 Java 和 C# 中有套件內可見性或內部可見性……或把它做成受保護的並通過一個測試基類來存取它。在 C++ 中也可以做類似的事情:可以把你的靜態方法設為受保護的,或引入一個命名空間。」(p.274)
  • 簽名保持是安全關鍵:「在沒有測試的情況下解依賴時,盡可能對方法進行簽名保持。對整個方法進行剪下/複製可以降低引入錯誤的可能性。」(p.273)
  • 與《重構》的差別:這條和 搬移函式 看起來像同一件事,但書中明說本章手法「在步驟上有所不同,我將之適當剪裁以使得它們能夠被安全地用在沒有測試的情況下」(p.258);設成靜態只是為了先寫得出測試,真正的搬家要等測試到位之後(p.273–274)。

🔗 相關工具