把「一個請求」連同它的參數封裝成獨立物件,讓你能把操作當資料傳遞、排隊、記錄與 undo。核心是讓發出請求的 invoker 不知道、也不在乎真正執行的是誰。
🧠 When to Use
TIP
訊號:你想把「要做的動作」存起來、排成佇列、寫進 log、之後重放或反悔(undo/redo),而不是當下立刻寫死地呼叫某個方法。
- 需要把操作參數化、放進佇列或排程,延後 / 在別的執行緒執行。
- 需要 undo/redo、巨集(一串命令)、或操作日誌(重播以還原狀態)。
- 想讓觸發者(按鈕、快捷鍵)與具體行為解耦——同一顆按鈕綁不同命令。
⚖️ Structure & Variants
經典版 — Command 介面 + Invoker,含 undo
interface Command { fun execute(); fun undo() }
class Light { var on = false }
class TurnOn(private val light: Light) : Command {
override fun execute() { light.on = true }
override fun undo() { light.on = false }
}
class Remote { // ← invoker,不知道具體命令
private val history = ArrayDeque<Command>()
fun press(cmd: Command) { cmd.execute(); history.addLast(cmd) }
fun undoLast() { history.removeLastOrNull()?.undo() }
}
val light = Light()
Remote().apply { press(TurnOn(light)); undoLast() } // on=falseRemote只認得Command;換行為 = 換傳進去的命令物件。
函數式版 — 命令就是一個 closure
- 不需要 undo 時,命令可以退化成一個
() -> Unit,把參數包進 closure 即可。 - 一旦要 undo/redo 或序列化,才值得升級成帶
execute/undo的具名物件。
巨集命令 — 把多個命令組成一個
MacroCommand內含一串Command,execute依序跑、undo逆序回放,本身也是Command(Composite 的味道)。
⚠️ Misuse & Anti-patterns
- 明明只是一次直呼還硬包成命令:沒有排隊 / undo / log 的需求,徒增物件與樣板。先 YAGNI。
- undo 沒存對狀態:靠重算反操作容易失真;需要完整快照時改搭 Memento 存還原點。
- 命令裡塞業務邏輯而非委派:命令應該是「薄薄的觸發器」,真正幹活的留給 receiver,否則邏輯散落、難測。
🔑 Takeaways
- 本質是「把請求變成物件」,於是操作能被傳遞、排隊、記錄與還原。
- Invoker 與 receiver 解耦:同一觸發點可綁不同命令,命令也能組成巨集。
- 簡單動作用 closure 就好;要 undo 還原狀態時搭 Memento。
No notes yet — jot your takeaways or Q&A here.