iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Software Development

文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念系列 第 18 篇

# Day 18:介面抽象化——為什麼軟體應該「依賴於介面而非實作」?

  • 分享至 

  • xImage
  •  

過去兩天,抽象介面在 OCP、DIP 和工廠模式裡反覆出現。今天要把「依賴於介面,而非依賴於實作」這句設計模式領域最早提出的核心原則單獨拆開來講清楚——它其實是前兩天所有做法背後共同的理由。

一、介面 vs. 實作

定義:介面(Interface)定義的是「能做什麼」——也就是方法名稱、輸入輸出的約定;實作(Implementation)則是「具體怎麼做到」的程式碼細節。依賴介面而非實作,意味著呼叫端只需要知道對方「能做什麼」,完全不需要知道對方內部「怎麼做到」。

二、為什麼要這樣做

  1. 降低耦合:呼叫端不會因為某個實作內部的細節改變(例如換了一套演算法、換了一個第三方套件)而被牽連著一起修改。
  2. 可替換性:只要新的實作符合同一份介面的約定,就能無痛替換掉舊的實作——像是把資料庫從 MySQL 換成 PostgreSQL、把通知管道從 Line 換成 Email,呼叫端的程式碼都不用改。
  3. 可測試性:測試的時候可以用一個假的(mock / stub)實作,取代真正連上資料庫或外部 API 的實作,讓測試跑得更快、更穩定。

三、代購 App 案例

延續 Day 16、17 的通知範例,這次換一個新的場景:訂單資料的儲存。如果 OrderService 直接依賴具體的 MySQLOrderRepository,未來想把資料庫換成 PostgreSQL,或是在測試時想用記憶體暫存的假資料,都得回頭去改 OrderService 本身的程式碼。

// repositories/orderRepository.js —— 定義介面(用約定的方法簽名表示)
class OrderRepository {
  save(order) { throw new Error('必須實作 save 方法'); }
  findById(id) { throw new Error('必須實作 findById 方法'); }
}

// repositories/mysqlOrderRepository.js —— 正式環境的實作
class MySQLOrderRepository extends OrderRepository {
  save(order) { /* 寫入 MySQL */ }
  findById(id) { /* 查詢 MySQL */ }
}

// repositories/inMemoryOrderRepository.js —— 測試用的假實作,同樣符合介面
class InMemoryOrderRepository extends OrderRepository {
  #orders = new Map();
  save(order) { this.#orders.set(order.id, order); }
  findById(id) { return this.#orders.get(id); }
}

// services/orderService.js —— 只依賴抽象的 OrderRepository,不管背後是哪個實作
class OrderService {
  constructor(orderRepository /* OrderRepository 的實例 */) {
    this.orderRepository = orderRepository;
  }
  createOrder(input) {
    const order = { ...input, total: input.price * input.quantity + input.shippingFee };
    this.orderRepository.save(order);
    return order;
  }
}

// 正式環境
const service = new OrderService(new MySQLOrderRepository());

// 測試環境:不用真的連資料庫,一樣可以測 OrderService 的邏輯
const testService = new OrderService(new InMemoryOrderRepository());

OrderService 的建構子只認得 OrderRepository 這個抽象約定,不管實際傳進來的是連著真實資料庫的 MySQLOrderRepository,還是測試用的 InMemoryOrderRepository,createOrder 這個方法裡的邏輯完全不用改一行。

四、結論

「依賴介面而非實作」不是一個全新的原則,而是把 Day 16 的 DIP 和 Day 17 的工廠模式背後共同的精神講得更直白——呼叫端只跟「約定」打交道,實際的實作可以隨時替換,不管是為了換技術、換廠商,還是單純為了測試,系統都不需要因此傷筋動骨。


上一篇
Day 17:模組化模式——模組匯出(ES Modules)、命名空間與工廠模式(Factory Pattern)基礎
系列文
文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言