Design Patterns in Swift: Command Pattern

Command pattern is a very simple pattern that allows you to decouple a class that invokes a command from a class that knows how to perform it. This gives you incredible flexibility. In this article we'll go over the theory of what command pattern is after which we'll follow-up with an example.Command pattern is a very simple pattern that allows you to decouple a class that invokes a command from a class that knows how to perform it. This gives you incredible flexibility. In this article we’ll go over the theory of what command pattern is after which we’ll follow-up with an example. We’ll also see how this pattern can be used to add some cool functionality, like undoable commands and transactions.

Command Pattern

The command pattern provides a way for you to wrap different kinds of objects in a same interface. This way you can decouple your objects from who ever is supposed to be using them allowing a class to use many different types of objects. Here’s the official definition:

Command pattern encapsulates a request as an object, thereby letting you parameterise clients with different requests, queue or log requests, and support undoable operations.

This might sound a bit confusing, let’s see it on a diagram.

The Actors on the Stage

This is a class diagram of the pattern:

Receiver is the class that’s doing the actual work. In your project this class will be your networking request, your database operation, your piece of business logic, a complex sort algorithm… In other words, your class, specific for your project.

Command is a protocol that will have just a handful of functions. In our example we’ll only have two functions: execute and undo. These commands will be handled by the Invoker.

Concrete Command is a class that conforms to the ‘Command’ protocol and it will take an instance of the receiver in the initialiser. When the invoker calls the ‘execute’ function this class will know what to do with the receiver. This class will also know how to perform an undo operation.

Invoker has a simple job of calling the ‘execute’ function on the commands. Normally the invoker will have an array of command objects and it will decide when to execute those commands. For example, it would be trivial to implement batch processing in the invoker as we’ll see later. The invoker can also call the ‘undo’ function if the command fails. One invoker can be used by many different clients.

Client is a class that will create the concrete command object and pass it into the invoker. Depending on your use case, the client can have a reference to the invoker and it can receive notifications when the queues/commands get processed.

Let’s check out another diagram to better understand the events and timings in our pattern.

The Chain of Events

Check out this sequence diagram:

  1. We start off with ‘aClient’ who is responsible for creating command objects. The client creates an instance of an object that conforms to the ‘Command’ protocol and has a reference to the receiver.
  2. After this, the client will pass this command to the invoker. Generally, at this point the client’s work is done.
  3. After an indefinite amount of time, the invoker will call the ‘execute’ function on the command. When this happens is entirely up to the invoker. It can happen immediately after the client adds the command to the invoker, or after some time. In our example we’ll process 5 commands at a time. Just to illustrate this point.
  4. When the invoker calls the ‘execute’ command the command object will start calling functions on the receiver. You can have many different types of commands and they will all know exactly what to do with the receiver. Of course, when the receiver receives commands stuff will happen 🙂

Enough of this theory, let’s see some code…

An Example

We’ll see how all this fits together on a simple example. In our example we’ll save a vehicle to the backend, database, log it to the file system and demonstrate how to use a transaction. We’ll be focusing on the use of the pattern here, so the methods don’t actually save anything anywhere, they just demonstrate the use of the pattern. Let’s get started…

Protocols

Obviously, the main protocol will be the command:

protocol Command {
    @discardableResult func execute() -> Result
    @discardableResult func undo() -> Result
}

And the invoker:

protocol Invoker {
    func addCommand(command: Command)
}

As you can see, these are very simple.

Receivers

In our diagrams these classes are called ‘Receivers’. In reality, these will be your existing classes in your projects. For example, one of your receivers might look like this:

enum VehicleAPIError: Error {
    case APIError
}

class VehicleAPI
{
    private let vehicle: Vehicle
    
    init(vehicle: Vehicle) {
        self.vehicle = vehicle
    }
    
    func sendToBackend() -> Result {
        print("Sending the vehicle '\(vehicle.name)' to backend!")
        
        // Simulate failure
        let success = false //Bool.random
        
        if success == true {
            print("Success")
            return .success
        } else {
            print("Failure")
            return .failure(VehicleAPIError.APIError)
        }
    }
    
    func deleteFromBackend() -> Result {
        print("Deleted the vehicle '\(vehicle.name)' from the backend")
        return .success
    }
}

How you construct them will be entirely up to you because they contain a lot of your custom logic. In this example we’re just printing stuff to the console and we have a simple bool switch so we can simulate API failures.

Next up, commands…

Commands

The commands will wrap your receivers, most of them will be really simple and they’ll just forward the method calls to the receiver:

class AddVehicleAPICommand: Command
{
    private var vehicleAPI: VehicleAPI
    
    init(api: VehicleAPI) {
        self.vehicleAPI = api
    }
    
    func execute() -> Result {
        return self.vehicleAPI.sendToBackend()
    }
    
    func undo() -> Result {
        return self.vehicleAPI.deleteFromBackend()
    }
}

Now things are getting interesting. A simple command like the one above is self-explanatory, but imagine having a macro command. A command that takes in an array of commands and executes them all. Or, even better, a transaction command like this one:

class TransactionCommand: Command
{
    private let commands: [Command]
    private var finishedCommands: [Command] = []
    
    init(commands: [Command]) {
        self.commands = commands
    }
    
    func execute() -> Result {
        
        print("Transaction started")
        for command in commands {
            let result = command.execute()
            if result == .success {
                finishedCommands.append(command)
            } else {
                print("Transaction failed: \(result)")
                finishedCommands.forEach { $0.undo() }
                finishedCommands.removeAll()
                return .failure(TransactionError.TransactionFailed)
            }
        }
        
        return .success
    }
    
    func undo() -> Result {
        return commands.reduce(Result.success, { (result, command) -> Result in
            guard result == .success else { return result }
            return command.undo()
        })
    }
}

This command will take an array of commands to execute. When you call ‘execute’ on it it will start to execute them in sequence. If a command fails it will call undo on all the previously executed commands. This will roll back the system to a state it was in before it started to execute the transaction. All this depends on your implementation of the ‘undo’ functions. If you didn’t implement them, then the system can’t be rolled back, obviously.

Not all commands need to implement the undo functionality:

class LogVehicleCommand: Command
{
    private let logger: VehicleLogger
    
    init(logger: VehicleLogger) {
        self.logger = logger
    }
    
    func execute() -> Result {
        self.logger.appendToLogfile()
        return .success
    }
    
    func undo() -> Result {
        print("Cannot undo a logging operation")
        return .success
    }
}

In the logger command above we’re only interested in creating an audit log. So we definitely don’t want to delete anything from the logs by implementing the ‘undo’ function.

Invoker

Commands on their own won’t really do much. To be honest, the only real benefit in the code we wrote so far is the transaction command and the ability to easily roll back. This brings us to the invoker. Invoker is the heart of the pattern. This is the class that will take in the commands and execute them when ever it sees fit. You can have some very complex logic in the invoker. You can run your commands in serial or in parallel. If you’re accessing a shared resource like a file, you can control access to it with the invoker. Let’s say you wanted your app to efficiently access the network, you could run your commands in batches. We’ll demonstrate this approach. Check out our batch invoker class:

class BatchInvoker: Invoker
{
    private enum Constants {
        static let BatchSize = 5
    }
    
    private var toBeExecuted: [Command] = [] {
        didSet {
            if toBeExecuted.count >= Constants.BatchSize {
                executeCommands()
            }
        }
    }
    
    private var finished: [Command] = []
    private var failed: [Command] = []
    
    func addCommand(command: Command) {
        toBeExecuted.append(command)
    }
    
    private func executeCommands() {
        toBeExecuted.forEach { (command) in
            if command.execute() == .success {
                finished.append(command)
            } else {
                failed.append(command)
            }
        }
        toBeExecuted.removeAll()
    }
}

As you can see, the class is pretty simple. It will store all the commands and as soon as it has 5 of them it will execute them. Additionally, it will save the finished and failed commands it the respective arrays. Like I said, you can decide for yourself what’s best for you and implement your own custom logic here.

Client

And, finally, the client. This could easily be your controller, your view controller, or some other class. It depends on the architecture you’re using. In this context this is a class that’s using the command pattern we’re discussing here. Client for our demo project looks like this:

class VehicleClient
{
    // This would be your normal controller, you'll have your business logic here.
    // Among other things this class will create your commands and pass them to the invoker.
    
    private let invoker = BatchInvoker()
    
    func startTesting() {
        
        // Just sending a vehicle to the API
        let vehicle = ATVehicle(id: 1, name: "first vehicle")
        let apiReceiver = VehicleAPI(vehicle: vehicle)
        let addVehicleCommand = AddVehicleAPICommand(api: apiReceiver)
        invoker.addCommand(command: addVehicleCommand)
        
        let dbReceiver = VehicleDAO(vehicle: vehicle)
        let saveVehicleCommand = SaveVehicleToDBCommand(dao: dbReceiver)
        invoker.addCommand(command: saveVehicleCommand)
        
        let logReceiver = VehicleLogger(vehicle: vehicle)
        let logVehicleCommand = LogVehicleCommand(logger: logReceiver)
        invoker.addCommand(command: logVehicleCommand)
        
        let addVehicleTransactionCommand = TransactionCommand(commands:
            [
                LogVehicleCommand(logger: logReceiver),
                SaveVehicleToDBCommand(dao: dbReceiver),
                AddVehicleAPICommand(api: apiReceiver),
                LogVehicleCommand(logger: logReceiver)
            ])
        invoker.addCommand(command: addVehicleTransactionCommand)
        
        let anotherLogReceiver = VehicleLogger(vehicle: ATVehicle(id: 2, name: "Another Vehicle"))
        let secondLogCommand = LogVehicleCommand(logger: anotherLogReceiver)
        invoker.addCommand(command: secondLogCommand)
    }
}

Our client has a reference to the invoker. In your case this might be different. Your invoker could be a singleton, for example. Ideally you would pass a reference to your invoker in the initialiser of the client. You can see that we only have one function and we’re using it for testing. We’re creating 5 commands in the test method, one of them is a transaction command. We’re only adding the commands to the invoker, we have no control over the execution. That’s pretty much if for the client. Let’s test it out.

Test Drive

If we just create an instance of our client and run the test method our output will look something like this:

Sending the vehicle 'first vehicle' to backend!
Success
Saved the vehicle 'first vehicle' to DB!
This class will append the logfile with a vehicle 'first vehicle'
Transaction started
This class will append the logfile with a vehicle 'first vehicle'
Saved the vehicle 'first vehicle' to DB!
Sending the vehicle 'first vehicle' to backend!
Success
This class will append the logfile with a vehicle 'first vehicle'
This class will append the logfile with a vehicle 'Another Vehicle'

But if we simulate an api failure by setting the flag in the ‘VehicleAPI’ class the output is s bit different:

Sending the vehicle 'first vehicle' to backend!
Failure
Saved the vehicle 'first vehicle' to DB!
This class will append the logfile with a vehicle 'first vehicle'
Transaction started
This class will append the logfile with a vehicle 'first vehicle'
Saved the vehicle 'first vehicle' to DB!
Sending the vehicle 'first vehicle' to backend!
Failure
Transaction failed: failure(ATCommandPattern.VehicleAPIError.APIError)
Cannot undo a logging operation
Deleted the vehicle 'first vehicle' from database
This class will append the logfile with a vehicle 'Another Vehicle'

Here we can see the transaction being rolled back due to the API failure. Pretty neat 🙂

Conclusion

You have another pattern under your belt. The command pattern is pretty interesting. If you think about it, it kinda reminds you of Operation and OperationQueue where the Operation is the command and the OperationQueue is the invoker.

The command pattern is usually used to construct menus, that was one of its first usages, actually. But I believe its biggest strength today is the ease of implementing a transaction mechanism. Implementing transactions as atomic operations is far from simple, but with this pattern it’s child’s play.

In this article we covered one of the potential usages of the pattern, there are loads more. Hopefully this will set you on your path and help you out in your projects. I hope you learned something new today and that the article was useful to you in some way. You can find all the code you see here in the GitHub repo. As usual…

Have a nice day 🙂
~D;

More resources

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.