Command Pattern
Command Pattern encapsulates a request to decouple sender and receiver.
classDiagram
direction LR
class Invoker {
+command : ICommand
+AddCommand(command : ICommand)
+ExecuteAll()
+Undo()
+PopCommand()
}
class ICommand {
+Execute()
+Undo()
}
class ConcreteCommand {
-receiver : Receiver
+Execute()
+Undo()
}
class Receiver {
+On()
+Off()
}
Invoker --> ICommand : "uses"
ICommand <|-- ConcreteCommand : "implements"
ConcreteCommand --> Receiver : "calls"
Introduction
命令模式有三個角色 Invoker, Receiver 和 Icommand 其主要目的是將請求與執行的動作分開
Implemetation
以下將以 golang 實作:
- ICommand (命令介面)
// Command interface that all commands will implement type Command interface { Execute() }
- Invoker (命令接收者/請求操作者)
// Invoker that will invoke the command type RemoteControl struct { command Command } func (r *RemoteControl) AddCommand(command Command) { r.commands = append(r.commands, command) } func (r *RemoteControl) Execute() { for _, cmd := range r.commands { cmd.Execute() } }
- Receiver (執行命令/實際操作者)
// Receiver of the command type Light struct { isOn bool } func (l *Light) On() { l.isOn = true fmt.Println("The light is on") } func (l *Light) Off() { l.isOn = false fmt.Println("The light is off") }
Code
// Concrete Command that implements the Command interface
type LightOnCommand struct {
light *Light
}
func (c *LightOnCommand) Execute() {
c.light.On()
}
// Concrete Command that implements the Command interface
type LightOffCommand struct {
light *Light
}
func (c *LightOffCommand) Execute() {
c.light.Off()
}
func main() {
// Create receiver
light := &Light{}
// Create command and associate it with the receiver
lightOnCommand := &LightOnCommand{light: light}
lightOffCommand := &LightOffCommand{light: light}
// Create invoker
remoteControl := &RemoteControl{}
// Set the command for the invoker and execute it
remoteControl.AddCommand(lightOnCommand)
remoteControl.AddCommand(lightOffCommand)
remoteControl.ExecuteAll()
}
Undo
// Command interface that all commands will implement
type Command interface {
Execute()
Undo() // add
}
func (c *LightOnCommand) Undo() {
c.light.Off()
}
func (c *LightOffCommand) Undo() {
c.light.On()
}
func (r *RemoteControl) PopCommand() {
if len(r.commands) > 0 {
r.commands = r.commands[:len(r.commands)-1]
}
}
func (r *RemoteControl) Undo() {
if len(r.commands) > 0 {
// Get last one cmd
cmd := r.commands[len(r.commands)-1]
cmd.Undo()
r.PopCommand() // Remove the command after undo
}
}
func main() {
// Create receiver
light := &Light{}
// Create command and associate it with the receiver
lightOnCommand := &LightOnCommand{light: light}
lightOffCommand := &LightOffCommand{light: light}
// Create invoker
remoteControl := &RemoteControl{}
// Set the command for the invoker and execute it
remoteControl.AddCommand(lightOnCommand)
remoteControl.AddCommand(lightOffCommand)
remoteControl.Execute()
remoteControl.Undo()
}
Redo
package main
import "fmt"
// Command 接口
type Command interface {
Execute()
Undo()
}
// LightOnCommand 具体命令
type LightOnCommand struct {
light *Light
}
func (c *LightOnCommand) Execute() {
c.light.On()
}
func (c *LightOnCommand) Undo() {
c.light.Off()
}
// Light 设备
type Light struct {
brightness int
}
func (l *Light) On() {
l.brightness = 100
fmt.Println("Light is on")
}
func (l *Light) Off() {
l.brightness = 0
fmt.Println("Light is off")
}
type RemoteControl struct {
commands []Command
undoStack []Command
redoStack []Command
}
// 添加命令
func (r *RemoteControl) AddCommand(command Command) {
r.commands = append(r.commands, command)
}
// 执行命令
func (r *RemoteControl) PressButton() {
for _, cmd := range r.commands {
cmd.Execute()
r.undoStack = append(r.undoStack, cmd)
}
r.commands = nil // 清空当前命令
r.redoStack = nil // 清空重做栈
}
// 撤销命令
func (r *RemoteControl) Undo() {
if len(r.undoStack) == 0 {
return
}
cmd := r.undoStack[len(r.undoStack)-1]
r.undoStack = r.undoStack[:len(r.undoStack)-1]
cmd.Undo()
r.redoStack = append(r.redoStack, cmd)
}
// 重做命令
func (r *RemoteControl) Redo() {
if len(r.redoStack) == 0 {
return
}
cmd := r.redoStack[len(r.redoStack)-1]
r.redoStack = r.redoStack[:len(r.redoStack)-1]
cmd.Execute()
r.undoStack = append(r.undoStack, cmd)
}
func main() {
light := &Light{}
lightOn := &LightOnCommand{light: light}
remote := &RemoteControl{}
remote.AddCommand(lightOn)
remote.PressButton() // 执行 LightOnCommand
remote.Undo() // 撤销 LightOnCommand
remote.Redo() // 重做 LightOnCommand
}
Conclusion
- Queue , Stack