把一個請求沿著「處理者鏈」往下傳,每個處理者自己決定要當場處理,還是丟給下一棒。發送者不必知道誰會處理、處理幾次,發送端與接收端因此解耦。
🧠 When to Use
TIP
訊號:同一個請求「可能由多個對象之一」處理,而你正想寫一串 if (canA) ... else if (canB) ... 來決定誰接手。
- 處理者集合與順序需要在執行期組裝、調整(如 middleware pipeline)。
- 一個請求依條件分級交給不同層級(如 log level、審批流:組長 → 經理 → 總監)。
- 想讓每個處理者只專注「我能不能處理、要不要放行」,不必認識彼此。
⚖️ Structure & Variants
經典版 — 鏈結節點,逐棒往下傳
abstract class Approver(private val next: Approver?) {
fun handle(amount: Int) {
if (canApprove(amount)) approve(amount)
else next?.handle(amount) // ← 自己處理不了就往下傳
}
protected abstract fun canApprove(amount: Int): Boolean
protected abstract fun approve(amount: Int)
}
class Manager(next: Approver?) : Approver(next) {
override fun canApprove(a: Int) = a <= 10_000
override fun approve(a: Int) = println("Manager approves $a")
}
val chain = Manager(/* next = */ Director(null))
chain.handle(5_000) // Manager 接手chain.handle的呼叫端不知道是誰批准的,也不需要知道鏈有多長。
Pipeline 版 — middleware 串接(攔截過濾器)
- Web 框架的 middleware/filter 鏈是同構:每一段拿到
request和「下一棒next」,可前處理、可呼叫next()、可短路。 - 和經典版差別:每段通常都會呼叫下一棒(過濾器鏈),而非「處理完就停」。
和 Decorator 的差別(結構都是串起來)
- Chain:每個節點可以「決定不往下傳」而中斷請求,重點在「誰處理」。
- Decorator:每層都會呼叫被包裹者,重點在「層層疊加行為」,不會吞掉請求。
⚠️ Misuse & Anti-patterns
- 沒人處理就靜默掉:鏈尾沒接手又不報錯,請求無聲消失最難查;至少要有預設處理者或丟例外。
- 把單純的多型硬包成鏈:如果根本不會有第二個處理者、順序也固定,直接呼叫對象就好,別憑空架一條鏈。
- 鏈的順序藏在各節點裡:誰接誰寫死在建構子裡,調整順序得改多處;把組裝集中到一個地方(builder/設定)才好維護。
🔑 Takeaways
- 本質是「請求沿鏈傳遞,每棒自己決定處理或放行」,解耦發送者與接收者。
- 和 Decorator 結構相似,差在「能不能中斷請求」。
- 想做 undo/可撤銷的請求時,常把鏈上傳遞的請求物件化成 Command。
No notes yet — jot your takeaways or Q&A here.