需求:設計一台販賣機(或任何「單一入口、背後一堆子系統」的裝置)。客戶端只想要
insertMoney()、selectProduct()這種簡單入口,但背後其實要協調庫存、金流、找零、交易紀錄。若把這些全塞進VendingMachine,它就變成 God Object。怎麼設計?
🎯 Scenario
// 反例:VendingMachine 一手包辦庫存、金流、找零、紀錄 → 上帝物件
class VendingMachine {
private val racks = mutableListOf<Rack>()
fun buy(code: String, cash: Money) {
val rack = racks.first { it.product.code == code } // 庫存邏輯
if (rack.count == 0) error("out of stock")
val change = cash - rack.product.price // 金流/找零邏輯
if (change < 0) error("insufficient")
rack.count-- // 分發+庫存
ledger.add(Transaction(code, cash, change)) // 交易紀錄
// …驗證、找零、紀錄全長在同一個類別裡,越長越肥
}
}
需求關鍵字:要對外提供一個簡單統一的入口,同時背後有多個各自有職責的子系統。這是 Facade 的主場——但關鍵是 facade 要輕、只委派。
🧠 Intuition
TIP
先別看下面。把「對外的入口」和「實際幹活的人」分開:VendingMachine 只當門面,把庫存交給 InventoryManager、金流交給 PaymentProcessor、把進行中的購買狀態收進 Transaction。門面本身不含業務細節。
⚖️ Reference Design
主解法 — Facade:輕量入口 + 委派給 SRP 元件
class InventoryManager(private val racks: List<Rack>) {
fun isAvailable(code: String) = racks.any { it.product.code == code && it.count > 0 }
fun dispense(code: String): Product { /* 遞減庫存、釋出商品 */ }
}
class PaymentProcessor {
fun charge(price: Money, cash: Money): Money { // 回傳找零
require(cash >= price) { "insufficient" }
return cash - price
}
}
// Facade:只協調、只委派,自己不含庫存/金流細節
class VendingMachine(
private val inventory: InventoryManager,
private val payment: PaymentProcessor,
) {
fun buy(code: String, cash: Money): Purchase {
require(inventory.isAvailable(code)) { "out of stock" }
val product = inventory.dispense(code)
val change = payment.charge(product.price, cash)
return Purchase(product, change)
}
}- 客戶端(機器的軟/硬體介面)只看到
VendingMachine一個簡單入口——這正是 Facade 的價值:為複雜子系統提供統一介面。 - 每個子系統各守一個職責(SRP):庫存、金流、交易資料分屬不同類別。
- 書中明講:為避免
VendingMachine淪為 god object,facade 應保持輕量、把任務委派出去。
設計取捨(書中的 Design Choice)
- 職責歸屬:
Product連結Rack(物理位置),而非反向——Product 是靜態實體,不該管自己的儲存;庫存量交給InventoryManager(SRP)。 - 分發不放在 Rack:
Rack只管庫存與商品資訊,分發動作上移到InventoryManager。 - 集合的封裝:管理 rack 集合時,優先給
addRack/removeRack這種粒度方法,並考慮 immutable collection / defensive copy,而非直接曝露整個 map。
Facade vs 相鄰模式 + 誤用
🔑 Takeaways
- 「單一簡單入口 + 背後多個子系統」→ 用 Facade:對外統一介面,對內把活委派給各守一職的元件。
- Facade 必須輕——一旦它開始含庫存/金流/驗證等業務細節,就退化成 God Object。
- 與 Adapter(轉介面求相容)、Mediator(同儕互相協調)分清意圖;職責歸屬按 SRP 切。
No notes yet — jot your takeaways or Q&A here.