許多系統的核心邏輯與第三方 API 呼叫混雜在一起——整個程式看起來就是一連串的 API 呼叫。這讓測試和重構變得極為困難,因為你無法輕易地將 API 依賴隔離出來。
核心問題#
當應用程式充滿 API 呼叫時:
- 難以測試:API 呼叫通常需要真實的外部資源(資料庫、網路、檔案系統)
- 邏輯被埋沒:業務邏輯散佈在 API 呼叫之間,難以獨立理解和測試
- 高度耦合:如果 API 改變,需要修改大量程式碼
解決策略:分離邏輯與 API 呼叫#
核心策略是將業務邏輯與 API 呼叫分離。即使程式碼看起來全是 API 呼叫,通常仍有隱藏的邏輯可以提取出來。
Skin and Wrap the API#
這是本章最重要的技術。有兩種主要方式:
方式一:Wrap API 呼叫#
為 API 建立一層 wrapper,讓你的程式碼依賴 wrapper 而非直接依賴 API:
- 建立一個 interface 來代表你需要的 API 功能
- 實作一個真正呼叫 API 的 wrapper 類別
- 在測試中使用 fake implementation
方式二:Skin the Application#
將業務邏輯提取到獨立的類別中,讓 API 呼叫留在一層薄薄的「皮膚」裡:
- 找出 API 呼叫之間隱藏的決策邏輯
- 將這些邏輯提取到可獨立測試的類別
- 讓 API 呼叫層變成簡單的委派
範例#
假設有一段充滿 API 呼叫的程式碼:
public class MailForwarder {
public void forwardMessage(Message message) {
// 一堆 API 呼叫混著業務邏輯
Session session = Session.getDefaultInstance(props, null);
MimeMessage forward = new MimeMessage(session);
forward.setFrom(getFrom(message));
// ... 更多 API 呼叫 ...
if (shouldForward(message)) {
Transport.send(forward);
}
}
}可以將其拆分為:
- 邏輯層:
shouldForward(message)及其他決策邏輯,可獨立測試 - API 層:實際的 mail API 呼叫,透過 interface 隔離

Figure 15.1: A better mailing list server
當你面對全是 API 呼叫的程式碼時,問自己:「這些呼叫之間有沒有決策?有沒有計算?」如果有,那些就是可以提取出來獨立測試的邏輯。
選擇哪種方式#
| 情境 | 建議方式 |
|---|---|
| API 較小且穩定 | Wrap the API |
| API 很大但邏輯可獨立抽取 | Skin the Application |
| 需要替換整個 API | Wrap the API |
| 邏輯與 API 高度交織 | 先 Skin,再逐步 Wrap |
關鍵不是選擇哪種方式,而是開始分離。即使一開始只能分離出一小部分邏輯,那也是有價值的第一步。隨著測試覆蓋率的提升,你可以逐步提取更多邏輯。
實務考量#
- 不要試圖一次重寫所有 API 呼叫:逐步地在需要改變的地方引入 wrapper
- Wrapper 可以從簡單開始:只包裝你目前需要測試的部分
- 保持 wrapper 層薄:Wrapper 不應該包含業務邏輯,它只是轉接層
- 善用 Lean on the Compiler:改變參數型別後,讓編譯器告訴你哪些地方需要更新