Card image cap

Exploring Design Patterns - Command

Design Patterns  • Posted 4 months ago

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 an invoker or a client to perform at a later stage.

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

There exist three actors here:

The Invoker - which book-keeps all the commands and the respective actions they perform.

The Command - which delegates incoming action request to the appropriate action performer.

The Receiver - which receives call for action from the command object and performs the necessary action over an entity or other.

And there is the Client, which is at the top of this flow, and from which the action requests originate. 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. Now 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). This 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.

For 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. For this, 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.

We use cookies to provide you with a great user experience, analyze traffic and serve targeted promotions.   Learn More   Accept