Aug 17, 2013

Command Design Pattern

1 comment
The command design pattern allows us to store a list of actions those we can execute later. A common example is, storing the undo actions in an application. The undo actions are stored as the user is making changes in an application. When the user decides to perform the undo, the undo actions are retrieved and executed.

The benefit of the command pattern is that, it hides the details of the actions that need to be performed, so that the client code does not need to be concerned about the details when it needs to execute the actions. The client code just needs to tell the application to execute the command that was stored.

In an undo example, if the user moved a rectangle from position X to position Y, then the undo action will be to move the rectangle from position Y back to position X. The details of this undo action is stored in the command objects so that when the user needs to execute the undo action, the application only need to tell the command object to execute its action without knowing that it is supposed to move the rectangle from position Y back to position X.

The command pattern is not focused so much on the sequence of the actions stored, but is more on hiding the details of the action it needs to perform.  

UML Diagram
  • The Client class uses the Invoker to run the commands.
  • The Invoker class stores the list of commands and can ask the ICommand to execute.
  • The ICommand interface defines the methods that all Command classes must implement. 
  • The Command class stores the details of the actions that need to be performed (the location of position Y and position X for the undo).
  • The Receiver class performs the action when called upon (moving the rectangle from position Y back to position X). 
The client code (calling code) will used the Invoker to run the commands, where the Command objects will call the Receiver to perform the action. The benefit is that the client code does not need to know what is stored in the Command objects nor the actions that will be performed by the Receiver, and this is the key of the Command Design Pattern.

Example
We need to store some undo actions when the user is using the application, and when the user decides to perform the undo we can just use the invoker to run the commands. Below is the UML for our undo example:
  • The UndoCommand class stores the location where the rectangle is supposed to move back.
  • The UndoPerformer will move the rectangle back to its original position, taking the UndoCommand as the parameter. 
  • The Invoker stores the list of undo commands.
 Implementation ICommand interface
using System;

namespace DesignPatterns.CommandPattern
{
    public interface ICommand
    {
        void Execute();
    }
}
UndoPerformer class
using System;
using System.Drawing;

namespace DesignPatterns.CommandPattern
{
    public class UndoPerformer
    {
        public void Undo(ICommand command)
        {
            if (command is UndoCommand)
            {
                Point originalLocation = (command as UndoCommand).Location;
                Console.WriteLine("Moving back to position: " + originalLocation);
            }
        }
    }
}
UndoCommand class
using System;
using System.Drawing;

namespace DesignPatterns.CommandPattern
{
    public class UndoCommand : ICommand
    {
        private Point _location;

        public Point Location
        {
            get { return _location; }
        }

        public UndoCommand(Point originalLocation)
        {
            _location = originalLocation;
        }
        
        public void Execute()
        {
            new UndoPerformer().Undo(this);
        }
    }
}
Invoker class
using System;
using System.Collections.Generic;

namespace DesignPatterns.CommandPattern
{
    public class Invoker
    {
        private Stack commandList = new Stack();

        public void RunCommand()
        {
            while (commandList.Count > 0)
                commandList.Pop().Execute();
        }

        public void AddCommand(ICommand command)
        {
            commandList.Push(command);
        }
    }
}
Client
private void btnCommand_Click(object sender, EventArgs e)
{
    Invoker invoker = new Invoker();
    
    // save undo to position (10, 20)
    ICommand cmd = new UndoCommand(new Point(10, 20));
    invoker.AddCommand(cmd);

    // perform the undo, the client does not need to know about the details of the undo
    invoker.RunCommand();
}
Output Moving back to position: {X=10,Y=20}

1 comment :