Skip to content

Command Design Pattern

Video Lecture

Section Video Links
Command Pattern Command Command Pattern 
Command Use Case Command Use Case Command Use Case 

Overview

...Refer to Book or Videos for extra content.

Terminology

...Refer to Book or Videos for extra content.

Command Pattern UML Diagram

The Command Pattern UML Diagram

Source Code

...Refer to Book or Videos for extra content.

./src/command/command-concept.ts

// The Command Pattern Concept

interface ICommand {
    execute(): void
}

class Invoker {
    // The Invoker Class
    #commands: { [id: string]: ICommand }

    constructor() {
        this.#commands = {}
    }

    register(commandName: string, command: ICommand) {
        // Register commands in the Invoker
        this.#commands[commandName] = command
    }

    execute(commandName: string) {
        // Execute any registered commands
        if (commandName in this.#commands) {
            this.#commands[commandName].execute()
        } else {
            console.log(`Command [${commandName}] not recognised`)
        }
    }
}

class Receiver {
    // The Receiver

    runCommand1() {
        // A set of instructions to run
        console.log('Executing Command 1')
    }

    runCommand2() {
        // A set of instructions to run
        console.log('Executing Command 2')
    }
}

class Command1 implements ICommand {
    // A Command object, that implements the ICommand interface and
    // runs the command on the designated receiver

    #receiver: Receiver

    constructor(receiver: Receiver) {
        this.#receiver = receiver
    }

    execute() {
        this.#receiver.runCommand1()
    }
}

class Command2 implements ICommand {
    // A Command object, that implements the ICommand interface and
    // runs the command on the designated receiver

    #receiver: Receiver

    constructor(receiver: Receiver) {
        this.#receiver = receiver
    }

    execute() {
        this.#receiver.runCommand2()
    }
}

// The Client
// Create a receiver
const RECEIVER = new Receiver()

// Create Commands
const COMMAND1 = new Command1(RECEIVER)
const COMMAND2 = new Command2(RECEIVER)

// Register the commands with the invoker
const INVOKER = new Invoker()
INVOKER.register('1', COMMAND1)
INVOKER.register('2', COMMAND2)

// Execute the commands that are registered on the Invoker
INVOKER.execute('1')
INVOKER.execute('2')
INVOKER.execute('1')
INVOKER.execute('2')

Output

node ./dist/command/command-concept.js
Executing Command 1
Executing Command 2
Executing Command 1
Executing Command 2

Command Use Case

...Refer to Book or Videos for extra content.

Example UML Diagram

The Command Pattern UML Diagram

Source Code

./src/command/client.ts

// The Command Pattern Use Case Example. A smart light Switch

import Light from './light'
import Switch from './switch'
import SwitchOnCommand from './switch-on-command'
import SwitchOffCommand from './switch-off-command'

// Create a receiver
const LIGHT = new Light()

// Create Commands
const SWITCH_ON = new SwitchOnCommand(LIGHT)
const SWITCH_OFF = new SwitchOffCommand(LIGHT)

// Register the commands with the invoker
const SWITCH = new Switch()
SWITCH.register('ON', SWITCH_ON)
SWITCH.register('OFF', SWITCH_OFF)

// Execute the commands that are registered on the Invoker
SWITCH.execute('ON')
SWITCH.execute('OFF')
SWITCH.execute('ON')
SWITCH.execute('OFF')

// show history
SWITCH.showHistory()

// replay last two executed commands
SWITCH.replayLast(2)

./src/command/light.ts

// The Light. The Receiver

export default class Light {
    turnOn(): void {
        // A set of instructions to run
        console.log('Light turned ON')
    }

    turnOff(): void {
        // A set of instructions to run
        console.log('Light turned OFF')
    }
}

./src/command/switch.ts

// The Switch (Invoker) Class.

import ICommand from './icommand'

export default class Switch {
    #commands: { [id: string]: ICommand }
    #history: [number, string][]

    constructor() {
        this.#commands = {}
        this.#history = []
    }

    showHistory(): void {
        // Print the history of each time a command was invoked"
        this.#history.forEach((row) => {
            console.log(`${row[0]} : ${row[1]}`)
        })
    }

    register(commandName: string, command: ICommand): void {
        // Register commands in the Invoker
        this.#commands[commandName] = command
    }

    execute(commandName: string): void {
        // Execute any registered commands
        if (commandName in this.#commands) {
            this.#commands[commandName].execute()
            this.#history.push([Date.now(), commandName])
        } else {
            console.log(`Command [${commandName}] not recognised`)
        }
    }

    replayLast(numberOfCommands: number): void {
        // Replay the last N commands
        const commands = this.#history.slice(
            this.#history.length - numberOfCommands,
            this.#history.length
        )
        commands.forEach((command) => {
            this.#commands[command[1]].execute()
            // or if you wanted to also record this replay in history
            // this.execute(command[1])
        })
    }
}

./src/command/icommand.ts

export default interface ICommand {
    execute(): void
}

./src/command/iswitch-command.ts

export default interface ISwitchCommand {
    execute(commandName: string): void
}

./src/command/switch-on-command.ts

import ISwitchCommand from './iswitch-command'
import Light from './light'

export default class SwitchOnCommand implements ISwitchCommand {
    #light: Light

    constructor(light: Light) {
        this.#light = light
    }

    execute(): void {
        this.#light.turnOn()
    }
}

./src/command/switch-off-command.ts

import ISwitchCommand from './iswitch-command'
import Light from './light'

export default class SwitchOffCommand implements ISwitchCommand {
    #light: Light

    constructor(light: Light) {
        this.#light = light
    }

    execute(): void {
        this.#light.turnOff()
    }
}

Output

node ./dist/command/client.js
Light turned ON
Light turned OFF
Light turned ON
Light turned OFF
1619288201312 : ON
1619288201313 : OFF
1619288201313 : ON
1619288201313 : OFF
Light turned ON
Light turned OFF

Summary

...Refer to Book or Videos for extra content.