DEV Community

Oluwasanmi Aderibigbe
Oluwasanmi Aderibigbe

Posted on • Updated on

Head First Design Pattern: 5 of 10

I just learnt my fifth design from the Head First Design Pattern book. Today, I learnt about the Command pattern.

According to Head First Design Patterns, the Command pattern is a pattern that encapsulate a request as an object, thereby letting you parameterise other object with different requests, queue and also undo requests.

The command pattern works by decoupling by the object making the request(invoker) and the object that knows how to perform the request. It does this by encapsulating actions into command objects. The command object houses the receiver(which essentially the object performing the action) and it's actions.

The first step is implementing is defining a command interface. This interface defines an method called execute.
The second is creating command objects that implement the command interface.
The third is is creating the invoker class, SmartHomeApp. SmartHomeApp has no knowledge of the actions being performed anytime OnButtonTapped and offButtonTapped.

The code below is an example of the command pattern.

interface Command {
    fun execute(): Unit
}

class TurnOnLightsCommand : Command {
    override fun execute() {
        print("Light turned on")
    }
}

class TurnOffLightsCommand : Command {
    override fun execute() {
        print("Light turned off")
    }
}

class TurnOnTVCommand : Command {
    override fun execute() {
        print("TV turned on")
    }
}

class TurnOffTVCommand : Command {
    override fun execute() {
        print("TV turned off")
    }
}

interface Command {
    fun execute(): Unit
}

class TurnOnLightsCommand : Command {
    override fun execute() {
        println("Light turned on")
    }
}

class TurnOffLightsCommand : Command {
    override fun execute() {
        println("Light turned off")
    }
}

class TurnOnTVCommand : Command {
    override fun execute() {
        println("TV turned on")
    }
}

class TurnOffTVCommand : Command {
    override fun execute() {
        println("TV turned off")
    }
}


class SmartHomeApp {
    private val onCommands = mutableListOf<Command>()
    private val offCommands = mutableListOf<Command>()

    fun setCommand(slot: Int, onCommand: Command, offCommand: Command) {
        onCommands.add(slot, onCommand)
        offCommands.add(slot, offCommand)
    }

    fun onButtonTapped(slot: Int) {
        onCommands[slot].execute()
    }

    fun offButtonTapped(slot: Int) {
        offCommands[slot].execute()
    }
}

fun main() {
    val smartHomeApp = SmartHomeApp()

    smartHomeApp.setCommand(0, TurnOnLightsCommand(), TurnOffLightsCommand())
    smartHomeApp.setCommand(1, TurnOnTVCommand(), TurnOffTVCommand())

    smartHomeApp.onButtonTapped(0)
    smartHomeApp.onButtonTapped(1)

}
Enter fullscreen mode Exit fullscreen mode

The command pattern can be used to implement really cool functionalities like undo, redo actions, performing a sequence of commands(Macros). It also allows you to queue and delay the execution of an action after request was made. With the command pattern you can also implement complex logging and state restoration logic.

Discussion (0)