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 實作:

  1. ICommand (命令介面)
    // Command interface that all commands will implement
    type Command interface {
        Execute()
    }
    
  2. 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()
        }
    }
    
  3. 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

  1. Queue , Stack

comments