iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
Software Development

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

Day 17:模組化模式——模組匯出(ES Modules)、命名空間與工廠模式(Factory Pattern)基礎

  • 分享至 

  • xImage
  •  

昨天談到 OCP 和 DIP,重點都放在「程式碼之間該怎麼依賴抽象」這個概念層次。今天要往下一層,看實際的程式碼要怎麼被組織成一個個可以獨立替換、匯入匯出的單位——也就是模組化的具體做法。

一、ES Modules:原生的模組化機制

定義:在 ES Modules 之下,每一個檔案本身就是一個獨立的作用域(scope)。沒有被明確 export 的內容,預設就是這個檔案的私有內容,不會外流;需要對外開放的部分,透過 import / export 明確宣告。

特點:

  1. 語言層級的隱私:不需要靠 IIFE 或命名慣例(例如加底線)來模擬私有變數,檔案本身就是天然的邊界。
  2. 具名匯出避免命名衝突:不同檔案可以各自定義同名的內部變數,互不干擾。
  3. 模組只會被評估一次:同一個模組不管被多少地方匯入,大家看到的都是同一份執行結果(binding)。
  4. 靜態的依賴宣告:import 語句在編譯時期就能被分析出來,讓工具可以做 tree-shaking(把用不到的程式碼移除)。

二、命名空間模式(Namespace Pattern)

定義:在 ES Modules 普及之前,為了避免把太多變數和函式直接暴露在全域環境,開發者會把相關的功能包在同一個物件底下,模擬出「分類」的效果:

const OrderUtils = {
  calculateTotal(price, qty, shippingFee) {
    return price * qty + shippingFee;
  },
  applyDiscount(total, rate) {
    return total * (1 - rate);
  }
};

現在 ES Modules 本身就提供了檔案級別的隔離,命名空間模式的必要性已經大幅降低。但當需要把一群邏輯相關的函式,包成一個看起來像「工具箱」的群組時,這種寫法仍然是一種清楚、可讀的組織方式。

三、工廠模式(Factory Pattern)

定義:用一個專門的函式(工廠函式)來負責「建立物件」這件事,呼叫端不需要知道物件是怎麼被建構出來的,只需要呼叫工廠函式、取得一個現成的實例即可。

目的:把「該建立哪一種具體實作」的判斷邏輯,集中在一個地方,其餘的呼叫端只需要面對抽象的結果,不用各自重複判斷。

四、代購 App 案例

延續昨天 NotificationChannel 的例子:如果要根據代購業者自己的設定,動態決定該用 Line 還是 Email 通知,呼叫端不應該自己寫一段 if/else 去判斷,而是把「決定用哪一種」的邏輯,交給一個工廠函式集中處理。

// notifications/lineNotificationService.js —— 各自獨立的模組
export class LineNotificationService extends NotificationChannel {
  notify(buyerId, message) { /* 呼叫 LINE API */ }
}

// notifications/emailNotificationService.js
export class EmailNotificationService extends NotificationChannel {
  notify(buyerId, message) { /* 呼叫 Email API */ }
}

// notifications/notificationFactory.js —— 工廠函式集中決定「要建立哪一種」
import { LineNotificationService } from './lineNotificationService.js';
import { EmailNotificationService } from './emailNotificationService.js';

export function createNotifier(channel) {
  if (channel === 'line') return new LineNotificationService();
  if (channel === 'email') return new EmailNotificationService();
  throw new Error(`未知的通知管道:${channel}`);
}

// orderService.js —— 呼叫端只需要一行,不管背後實際建立的是哪個 class
import { createNotifier } from './notifications/notificationFactory.js';

const notifier = createNotifier(merchant.preferredChannel);
notifier.notify(order.buyerId, `訂單總額 ${total} 元`);

如果之後又要新增第三種通知管道,只需要在 notificationFactory.js 裡多加一行判斷、新增一個對應的檔案,orderService.js 完全不用被碰到——這跟昨天 OCP 講的「對擴充開放、對修改封閉」是同一件事,只是這次發生在「物件怎麼被建立」這個環節。

五、結論

ES Modules 負責的是「檔案層級的封裝」,工廠模式負責的則是「物件建立邏輯的封裝」——兩者搭配起來,呼叫端拿到的永遠是一個符合抽象介面的實例,不需要知道實際上是哪個檔案、哪個 class 在背後運作。這正是昨天 DIP 講的「依賴抽象」,在真實的檔案結構與程式碼組織上的具體落實。


上一篇
Day 16:SOLID 原則入門(二)——開放封閉原則(OCP)與依賴反轉原則(DIP)的抽象思維
下一篇
# Day 18:介面抽象化——為什麼軟體應該「依賴於介面而非實作」?
系列文
文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言