How to use Command Design Pattern made Easy

In this detailed article, let us discuss what is Command Design pattern and how to implement it with a simple example in C# language

Introduction

The Command pattern is one of the 23 design patterns which solve a variety of problems which occur during a robust and scalable software design and development.

The Command pattern is a behavioral pattern which defines how an object can be encapsulated with all information needed to perform an action and which can be used by a client to perform at a later stage.

What is Command Pattern?

The pattern defines three kinds of components and the interaction that happens between them which help in decoupling an action performer from its initiator and the actual receiver without having each other to know about themselves in an abstract way.

There exist three actors here:

  1. Invoker – which book-keeps all the commands and the respective actions they perform.
  2. Command – which delegates incoming action request to the appropriate action performer.
  3. Receiver – which receives call for action from the command object and performs the necessary action over an entity or other.

The Client is at the top of this flow, and from which the action requests originate.

How does a Command pattern work?

The client sends out the request for some functionality to be performed over to the Invoker, which is an abstraction of all the functionalities and the encapsulated Command objects which own them.

The Invoker internally has mapping of what Command needs to be invoked for any incoming functionality request (say place an order, fetch a record or something).

Invoker then pulls out the respective Command object and then invokes an Execute() method on the object, which internally delegates this functionality call to the appropriate Performer or Receiver that contains the functionality.

In this way, we can create encapsulations of a single functionality into its commands and delegates so that they could be swapped or enhanced without having the client be informed of the change.


Quicklly

Implementing Command Pattern with an Example

Consider a sample Client class which would need to place an Order as a part of an Order Management module. When we call the PlaceOrder() method with some order details, a new order needs to be added to the records.

Instead of having the Order functionality be developed directly, we add our Invoker-Command-Receiver approach as below.

First let’s create the abstract IPlaceOrderCommand class which represents a Command object to place Order. And the abstract IPlaceOrderAction class which represents the action of an order placed.

// The Command template
public interface IPlaceOrderCommand
{
    void Execute();
}

// The Action Receiver template
public interface IPlaceOrderAction
{
    int Place(Order order);
}

Then we create concrete implementations of the Command and Action abstractions.

// Some Concrete Implementation of the Command
public class PlaceOrderCommand : IPlaceOrderCommand
{
    private readonly IPlaceOrderAction _action;
    private readonly Order _order;

    public PlaceOrderCommand(IPlaceOrderAction action, Order order)
    {
        _action = action;
        _order = order;
    }

    public void Execute()
    {
        _action.Place(_order);
    }
}

// Some Concrete Implementation of the Receiver Action
public class PlaceOrderAction : IPlaceOrderAction
{
    private readonly List<Order> _orders = new List<Order>();

    public int Place(Order order)
    {
        _orders.Add(order);
        return _orders.Count;
    }
}

For simplicity sake, we add the received Order object from the Command to a local collection and return the latest record Id.

Now that we have the Command and Receiver ready, the Invoker shall be of the form of an OrderManager that offers a variety of functionalities to the outside client. And this OrderManager invoker class receives the Command objects it needs to process in terms of their abstractions, so that they can be changed whenever needed.



// The Command Invoker template
public interface IOrderInvoker
{
    void PlaceOrder();
    Order FetchOrder(int orderId);
    void DeleteOrder(int orderId);
}

// some concrete implementation of the Command Invoker
public class OrderManager : IOrderInvoker
{
    private readonly IPlaceOrderCommand _place;

    // other command objects representing 
    // a single responsibility each
    private readonly IFetchOrderCommand _fetch;
    private readonly IDeleteOrderCommand _delete;

    public OrderManager(
        IPlaceOrderCommand place,
        IFetchOrderCommand fetch,
        IDeleteOrderCommand delete)
    {
        _place = place;
        _fetch = fetch;
        _delete = delete;
    }

    public void PlaceOrder()
    {
        // invoke the command
        _place.Execute();
    }

    public Order FetchOrder(int orderId)
    {
        return _fetch.Execute(orderId);
    }

    public void DeleteOrder(int orderId)
    {
        _delete.Execute(orderId);
    }
}

And finally in the Client, we decide what implementation an Action should provide and what Command should possess what Actions. And we fill the OrderManager with this knowledge and call the functionality we need.



public class CommandClient
{
    private IFetchOrderCommand _fetch;
    private IDeleteOrderCommand _delete;
    private IPlaceOrderCommand _place;

    public void DoPlaceOrder(int orderId, string productName, int quantity)
    {
        // create a new order
        var order = new Order(orderId, productName, quantity);

        // setup what action the command performs
        var action = new PlaceOrderAction();

        // setup the command with the action
        _place = new PlaceOrderCommand(action, order);

        // setup the invoker with what command to perform
        var orderManager = new OrderManager(_place, _fetch, _delete);

        // the client uses the invoker to do the action
        orderManager.PlaceOrder();
    }
}

In this way, we can provide a clear abstraction of the actions from their invocations via the Command pattern.


Buy Me A Coffee

Found this article helpful? Please consider supporting!

Ram
Ram

I'm a full-stack developer and a software enthusiast who likes to play around with cloud and tech stack out of curiosity. You can connect with me on Medium, Twitter or LinkedIn.

Leave a Reply

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