Mastering Swift's Development Blog

Follow Us On Twitter
  • Jon Hoffman

Command Pattern: Protocol Oriented Design Pattern

Updated: Sep 8


The command pattern is a behavior design pattern where we encapsulate all the information and logic necessary to perform actions into stand-alone types. We are then able to use these types interchangeably which enables us to swap them out at run time depending on the need. The command pattern is very similar to the strategy pattern. The difference is with the command pattern we are swapping out types that change functionality while with the strategy pattern we are swapping out types that perform the same function just in different ways.


Problem We Are Trying To Solve

In our applications, there are times where we need to separate the execution of a command from the code that invokes it. Usually this is when we have a type that needs to perform one of several actions, however which action to perform needs to be determined at runtime.


Our Solution

The command pattern tells us that we should encapsulate the logic for the actions into types that adopt a specific protocol. We can then use instances of any of these types, using the interface defined by the protocol, to perform the specific actions. Using the interface defined by a protocol to interact with instances of different types, is a form of polymorphism.


The Example

In this example we have a game controller which has multiple buttons that can be programmed to perform different actions in our game. The user can swap out which action works with which button throughout the game. As we develop the game we will be adding additional actions. Each button also has a back light that is turned on when the button is active and has an action assigned to it. This following illustration shows how the command pattern can be used to solve this problem.






As usual in our design pattern tutorials, we are going to be showing the minimal amount of code necessary to demonstrate this pattern because we want the focus of this post to be on how the command design pattern works and not on implementing the example.


As we do with any protocol-oriented design, we will start off with the protocol. In this case we will be starting with the following protocol which defines the interface to use for the button actions:

protocol GameControllerButton {
    func onButtonPressed()
    func onButtonReleased()
}

Types that adopt the GameControllerButton protocol will incapsulate the functionality to perform an action when a button is pressed and when the button is released. This functionality will be embedded within the onButtonPressed() and onButtonReleased() functions. One other functionality that is defined in our requirements is turning the back light on or off depending on if the button is active however this functionality will be common for all button types which makes it perfect to define within a protocol extension. This extension would take the following form:

extension GameControllerButton {
    func turnBackLightOn() {
        // Code to turn light on
    }
    func turnBackLightOff() {
        // Code to turn light off
    }
}

Now that we have defined the protocol and the protocol extension lets create the game controller type. This game controller has two properties which can take instances of any type that adopts the GameControllerButton protocol. This will enable us to change out the action for the buttons at runtime. Here is the code for the game controller

struct GameController {
    private var button1: GameControllerButton?
    private var button2: GameControllerButton?
    
    mutating func setButton1(_ button1: GameControllerButton?) {
        self.button1?.turnBackLightOff()
        self.button1 = button1
        self.button1?.turnBackLightOn()
    }
    
    mutating func setButton2(_ button2: GameControllerButton?) {
        self.button2?.turnBackLightOff()
        self.button2 = button2
        self.button2?.turnBackLightOn()
    }
    
    func button1Pressed() {
        button1?.onButtonPressed()
    }
    func button1Released() {
        button1?.onButtonReleased()
    }
    func button2Pressed() {
        button2?.onButtonPressed()
    }
    func button2Released() {
        button2?.onButtonReleased()
    }
}

In the GameController type we define two buttons where each will take an instance of a type that adopts the GameControllerButton protocol. These buttons are defined as optional in case we do not have an action assign to a button. There are two functions defined which are used to set these properties. Within these functions we use the turnBackLightOff() and turnBackLightOn() functions to toggle the button lights. There are also several functions to handle when the button is pressed and when it is released.


Now that we have created the game controller that will use instances of types that adopt the GameControllerButton let’s create a few of these types. The following are three examples.

struct JumpButton: GameControllerButton {
    func onButtonPressed() {
        // Code to execute jump
    }
    func onButtonReleased() {
        // Code on button release
    }
}

struct FireButton: GameControllerButton {
    func onButtonPressed() {
        // Code to execute fire gun
    }
    func onButtonReleased() {
        // Code on button release
    }
}

struct DuckButton: GameControllerButton {
    func onButtonPressed() {
        // Code to execute duck
    }
    func onButtonReleased() {
        // Code on button release
    }
}

Each of these three types adopt the GameControllerButton protocol and will perform a specific function when the button is pressed or released. For example, if the user presses the button which is assigned an instance of the DuckButton type, the character will duck and when the button is released the character will get back up. Now let’s see how we would use these types with the game controller.

var gameController = GameController()

gameController.setButton1(JumpButton())
gameController.setButton2(DuckButton())

gameController.button1Pressed()

//later in the game

gameController.setButton2(FireButton())
//Button2 will now fire a weapon

This code starts off by creating an instance of the GameController type and then set button1 to an instance of the JumpButton type and button2 to an instance of the DuckButton type. Later in the game, as the game progresses, we change button2 to an instance of the FireButton type.


As we develop the game, it is also very easy to add additional actions for our buttons in the form of new types that adopt the GameControllerButton protocol. Once these new types are created, we can then use them within our game controller.


This pattern is very useful when we need the ability to swap functionality out at run time. It also gives us very flexible code because we can very easily add additional types to perform different actions.


masteringSwift.jpeg

Mastering Swift 5.3

The sixth edition of this bestselling book, updated to cover through version 5.3 of the Swift programming language

Amazon

Packt

pop.jpeg

Protocol Oriented Programming

Embrace the Protocol-Oriented design paradigm, for better code maintainability and increased performance, with Swift.

Amazon

Packt