The Command design pattern encapsulates a request as an object, thereby allowing us developers to treat that request differently based upon what class receives said command. In addition, it enables much more complex architectures, and even operations such as undo and redo.
The Chain of Responsibility pattern fits well with the Command pattern, as the former can use objects of the latter to represent its requests.
NOTE: This post is part of a series demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Check out the other posts in this series!
The Rundown
- Type: Behavioral
- Useful? 4/5 (Very)
- Good For: Encapsulating requests as objects so that they can be processed differently by different receivers.
- Example Code: On GitHub
The Participants
- The Command declares an interface for executing an operation.
- The ConcreteCommand defines a binding between a Receiver and an action.
- The Client creates a ConcreteCommand object and sets its receiver.
- The Invoker asks the command to carry out its request.
- The Receiver knows how to perform the operations associated with carrying out the request.
A Delicious Example
Since just defining the Participants doesn't do a very thorough job of explaining what this pattern is all about, let's build our demo project. In this project, we'll model a system in which we can create an order for a fast food restaurant, and add, remove, and modify items in the order using the Command design pattern.
To begin building our demo, let's first create a class MenuItem
which represents an item being ordered.
/// <summary>
/// Represents an item being ordered from this restaurant.
/// </summary>
public class MenuItem
{
public string Name { get; set; }
public int Amount { get; set; }
public double Price { get; set; }
public MenuItem(string name, int amount, double price)
{
Name = name;
Amount = amount;
Price = price;
}
public void Display()
{
Console.WriteLine("\nName: " + Name);
Console.WriteLine("Amount: " + Amount.ToString());
Console.WriteLine("Price: $" + Price.ToString());
}
}
Since those items will be ordered by a patron of the restaurant, let's also create a Patron
object which will be our Invoker participant. It just so happens that our implementation of the Invoker also includes an implementation of the factory method pattern:
/// <summary>
/// The Invoker class
/// </summary>
public class Patron
{
private OrderCommand _orderCommand;
private MenuItem _menuItem;
private FastFoodOrder _order;
public Patron()
{
_order = new FastFoodOrder();
}
public void SetCommand(int commandOption)
{
_orderCommand = new CommandFactory().GetCommand(commandOption);
}
public void SetMenuItem(MenuItem item)
{
_menuItem = item;
}
public void ExecuteCommand()
{
_order.ExecuteCommand(_orderCommand, _menuItem);
}
public void ShowCurrentOrder()
{
_order.ShowCurrentItems();
}
}
public class CommandFactory
{
//Factory method
public OrderCommand GetCommand(int commandOption)
{
switch (commandOption)
{
case 1:
return new AddCommand();
case 2:
return new ModifyCommand();
case 3:
return new RemoveCommand();
default:
return new AddCommand();
}
}
}
Note that the Patron
keeps a reference to an instance of FastFoodOrder
, which is our Receiver participant and is implemented like so:
/// <summary>
/// The Receiver
/// </summary>
public class FastFoodOrder
{
public List<MenuItem> currentItems { get; set; }
public FastFoodOrder()
{
currentItems = new List<MenuItem>();
}
public void ExecuteCommand(OrderCommand command, MenuItem item)
{
command.Execute(this.currentItems, item);
}
public void ShowCurrentItems()
{
foreach(var item in currentItems)
{
item.Display();
}
Console.WriteLine("-----------------------");
}
}
FastFoodOrder
keeps track of all items in the order, so that when commands arrive at it, it can process those commands using its own list of items.
Speaking of the commands, let's implement the base Command participant, the class OrderCommand
:
/// <summary>
/// The Command abstract class
/// </summary>
public abstract class OrderCommand
{
public abstract void Execute(List<MenuItem> order, MenuItem newItem);
}
Now we can also implement several ConcreteCommand objects:
/// <summary>
/// A concrete command
/// </summary>
public class AddCommand : OrderCommand
{
public override void Execute(List<MenuItem> currentItems, MenuItem newItem)
{
currentItems.Add(newItem);
}
}
/// <summary>
/// A concrete command
/// </summary>
public class RemoveCommand : OrderCommand
{
public override void Execute(List<MenuItem> currentItems, MenuItem newItem)
{
currentItems.Remove(currentItems.Where(x=>x.Name == newItem.Name).First());
}
}
/// <summary>
/// A concrete command
/// </summary>
public class ModifyCommand : OrderCommand
{
public override void Execute(List<MenuItem> currentItems, MenuItem newItem)
{
var item = currentItems.Where(x => x.Name == newItem.Name).First();
item.Price = newItem.Price;
item.Amount = newItem.Amount;
}
}
Since we now have all the pieces in place, let's create our Client participant which creates a ConcreteCommand and sets the receiver. We will also add several items to our order, then delete an item and change another item.
static void Main(string[] args)
{
Patron patron = new Patron();
patron.SetCommand(1 /*Add*/);
patron.SetMenuItem(new MenuItem("French Fries", 2, 1.99));
patron.ExecuteCommand();
patron.SetCommand(1 /*Add*/);
patron.SetMenuItem(new MenuItem("Hamburger", 2, 2.59));
patron.ExecuteCommand();
patron.SetCommand(1 /*Add*/);
patron.SetMenuItem(new MenuItem("Drink", 2, 1.19));
patron.ExecuteCommand();
patron.ShowCurrentOrder();
//Remove the french fries
patron.SetCommand(3 /*Remove*/);
patron.SetMenuItem(new MenuItem("French Fries", 2, 1.99));
patron.ExecuteCommand();
patron.ShowCurrentOrder();
//Now we want 4 hamburgers rather than 2
patron.SetCommand(2 /*Edit*/);
patron.SetMenuItem(new MenuItem("Hamburger", 4, 2.59));
patron.ExecuteCommand();
patron.ShowCurrentOrder();
Console.ReadKey();
}
As the orders are processed by the Receiver (the FastFoodOrder
class), the contents of the order changes. Here's the output for this sample project:
Will I Ever Use This Pattern?
I have, but you will probably not, unless you are using more complex architectures.
In my case, my team and I were building an app using command-query responsibility segregation and event sourcing, two complex architectures which, together, are implementations of the Command design pattern blown up to support large, intricate projects.
The Command pattern is a rather useful pattern, but invokes a lot of complexity (more so than many other design patterns) so use the it with the requisite caution.
Summary
The Command pattern seeks to encapsulate commands as objects and allow different receivers to process them, according to the receivers' own design.
Happy Coding! And don't eat too much fast food, now, ya hear?