Command pattern

 - 

Few patterns are as versatile as the command pattern. At it...

Command pattern

Created: 12/30/2021Updated: 12/30/2021

Few patterns are as versatile as the command pattern. At its core, implementing this pattern is an act of isolating units of business logic behind a standard interface. In this article, I'll demonstrate this pattern's versatility and effectiveness with practical examples.


Definition

The command pattern is defined in Design Patterns [1], as a "behavioral" pattern intended to, "Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations."

A couple reasons to use this pattern?

Extensibility

The command pattern allows you to separate, or decouple, a piece of business logic from the context of where it may be referenced. A standardized interface is key to pulling this off. The typical command interface is as simple as Execute(): void. The reason for this simplicity is because the goal is to be able to treat the functionality in the command as a generic request. A complex interface would reduce the usability of the commands.

Diagram of a command pattern implementation

Look at the above diagram for a moment. What advantages may the command pattern shown here give us over just having each one of those commands as a private method on the SessionManager?

The one word answer is, "extensibility," but let's expand on that. Consider that you may be building an application with multiple "SessionManagers." The command pattern would work well in that scenario, though a base class or injected dependency with those "commands" may do the job just as well.

Let's take it a step further. Now pretend you are a contractor. You've been hired to build a library that integrates with an underlying session engine which will be consumed by a different application or applications. The client wants your library to expose a given list of operations, but leave sequence, error handling, and other concerns to the dependent project. At this point, the command pattern is still viable, whereas the others we've discussed are not.

As with many patterns, as the need for extensibility grows, the command pattern becomes more and more attractive.

Surrounding business logic

Logging and the ability to "rollback" transactions are also frequent justifications for the command pattern. A generic interface around any given operation allows for generic business logic to be written once on the command vice with each operation.

A simpler command pattern example, where the command implementation merely wraps a given function.

Consider the above diagram, which is quite a bit simpler than the one in the last example. What value could we get in wrapping a given behavior like this?

Let's say you're building out part of an application where stability is king, but the "features" you're adding amount to user analytics tracking. In order to track the user's behavior so we can make those "data driven decisions" your new code is making a lot of calls to third party libraries, which you clearly have less control over. It's imperative that any errors from calling those analytics libraries NEVER bubble up to the application.

The below code snippet (TypeScript) shows how this "surrounding" business logic can be encapsulated and reused via the command pattern.

classCommand {  constructor(private func: () => void) {}  publicExecute(): void {    try {      this.func();    } catch (error) {      console.error(error);    }  }}newCommand(() => {    /* do scary stuff with a third party dependency */}).Execute();

This might seem like a contrived example, but consider how, vice error handling, this could instead be taking snapshots of state for rollback purposes, interacting with application level loggers, or just about anything else.

In fact, there are closely related patterns for wrapping interactions between systems, especially databases, that carry the same, or similar, name. The Command Query Responsibility Segregation (CQRS) pattern is worth looking into. Microsoft has a good article on that pattern here. Keep in mind though, that this is an extension on the base Command pattern we're discussing in this article.

Monopoly example

Consider writing the business logic for the board game, Monopoly. You can't easily define an interface for the spaces on the board can you? Some spaces represent properties the player can buy, or perhaps needs to pay rent on. Some represent "chances" for rewards or setbacks. House rules aside, free parking doesn't do much of anything. What about that "go to jail" space? Pretty varied functionality, huh?

Let's explore how the command pattern can help us in this "Monopoly" scenario.

Monopoly has 40 spaces, including 28 properties, and several other unique types of spaces with varying game mechanics [2]. Many of those game mechanics manipulate the state of the game board, the current player, and other players. This makes consistent parameterization of a method difficult, since we may or may not need a reference to the game board, various players, etc. The command pattern, and a bit of dependency injection, will handle this problem nicely.

Consider this snippet (C#):

interfaceICommand{    voidExecute();}interfaceISpace{    ICommand[] Commands { get; set; }}interfaceIPlayer{    int Balance { get; set; }    ISpace CurrentSpace { get; set; }}classPayIncomeTaxCommand : ICommand{    privatereadonly IPlayer currentPlayer;    publicPayIncomeTaxCommand(IPlayer currentPlayer)    {        this.currentPlayer = currentPlayer;    }    publicvoidExecute()    {        currentPlayer.Balance -= 200;    }}classGoToJailCommand : ICommand{    privatereadonly ISpace goToJailSpace;    privatereadonly IPlayer currentPlayer;    publicGoToJailCommand(ISpace goToJailSpace, IPlayer currentPlayer)    {        this.goToJailSpace = goToJailSpace;        this.currentPlayer = currentPlayer;    }    publicvoidExecute()    {        currentPlayer.CurrentSpace = goToJailSpace;    }}

Notice how the command interface remains as simple as it gets, despite the functionality of the two command implementations being very different. The pay income tax command only needs a reference to the current player so that it can decrease their balance by 200. The go to jail command, however, needs a reference to the go to jail space as well so that it can update the current player's current space property.

The command pattern, using dependency injection, is allowing us to maintain best practices and keep these bits of business logic isolated and easily testable without complicating the logic for whatever is responsible for executing the commands. Presumably a higher level abstraction would merely have to call Execute() on each command associated with a given space. Any selection (if/else, etc.) would be encapsulated within the command's implementation and not affect callers of Execute.

There's obviously a ton more we could do with Monopoly. The Chance and Community Chest cards would also be a natural fit for the command pattern. Imagine having a collection of ChanceCommands randomized and stored in a queue at the start of a game. You could then grab the next command and execute it during the game quite intuitively.

Summary

The command pattern is ubiquitous.

Chance are, if you've been programming for a year or more, that you've actually used the command pattern quite a few times. The interface may have differed a bit from the void Execute() interface used in the examples in this article, however, if you can think of a time where you've implemented business logic behind a consistent interface so that it can be executed by some higher level abstraction, it's likely you were using the command pattern.

Given its simplicity, extensibility, and commonality, I'd recommend considering the command pattern as a basic tool of the trade.

Credits

  1. Design Patterns, Gamma et al. (1994)
  2. Wikipedia: Monopoly (game)