Introduction
The idea of this article is to understand what is a Command Pattern,
when is it useful and how can we get a basic implementation of Command pattern in place.
Background
There are many a times in our application that we want to perform multiple
operations on same data. I mean the
scenarios when the user has some data and he has an option of
doing one out of many operations in that data.
Example could be an image processor, I could choose to rotate,
flip and/or invert colors of the photo. Also I could
find myself not happy with the action and I may want
to undo my action. Now this is all business logic that
I had to place in my code. Now what I could do is to have my
action invoker call the methods of action
reciever/taker and achieve this. but then I will have to write a
lot of conditional code. I will have to write very
similar code for each action. Also, If I have more commands I find
myself changing the existing code very often.
Instead of taking the above approach, I could devise a
mechanim where all my actions can somehow be handled
polymorphically and thus giving me a clean and less error
prone code/design. This idea is the basic intent of
writting a Command Pattern.
Command Pattern talk about encapsulating all the requests
coming from the invoker in Objects, pass then to the
reciever and let the reciever take the actions. If I
have many actions to perform, I can haVe a hierarchy of
commands and handle them polymorphically.
GoF defines Command Pattern as "Encapsulate a request as an object,
thereby letting you parameterize clients with
different requests, queue or log requests, and support
undoable operations." So let us look at the class diagram
for the command pattern and try to understand it.
Command
: This is an interface for executing an action
ConcreteCommand
: This object specifies the binding between a Receiver/action
taker and an action invoker. This object is responsible for
executing corresponding operationon Receiver.
Client
: creates a ConcreteCommand object and sets its receiver
Invoker
: It will use Command object to carry out the request
Receiver
: It contains the real operational logic that need to be performed on the data.
Using the code
Let us now see how to have a simple Command pattern
implementation by writting a small Calculator sort of
application. We will focus on understanding command pattern
so we will operate on fix set os data and will try to
perform basic oeprations on them. Lets say we will be creating an appliction like this
Note: The idea here is to understand the Command Pattern
and not writting a calculator. Also, this example is small
so Command pattern might seem overkill for this.
So let us start by writting the Command
class.
abstract class ACommand
{
protected IReciever reciever_ = null;
public ACommand(IReciever reciever)
{
reciever_ = reciever;
}
public abstract int Execute();
}
This class is an abstract class and will be used as an interface
for execution of commands. Let us now write the
ConcreteCommand
classes that will inherit this abstract class.
class AddCommand : ACommand
{
public AddCommand(IReciever reciever)
:base(reciever)
{
}
public override int Execute()
{
reciever_.SetAction(ACTIO_LIST.ADD);
return reciever_.GetResult();
}
}
class SubtractCommand : ACommand
{
public SubtractCommand(IReciever reciever)
: base(reciever)
{
}
public override int Execute()
{
reciever_.SetAction(ACTIO_LIST.SUBTRACT);
return reciever_.GetResult();
}
}
class MultiplyCommand : ACommand
{
public MultiplyCommand(IReciever reciever)
: base(reciever)
{
}
public override int Execute()
{
reciever_.SetAction(ACTIO_LIST.MULTIPLY);
return reciever_.GetResult();
}
}
So now we have the command hierarchy for our application
ready. Now let is work on writting the real application
logic. We will be needing a class that will do the calculations
i.e. The Reciever
. lets have an interface to
represent the major functionalities of the reciever.
enum ACTIO_LIST
{
ADD,
SUBTRACT,
MULTIPLY
}
interface IReciever
{
void SetAction(ACTIO_LIST action);
int GetResult();
}
and the concrete Implementation of the Reciever
i.e. our Calculator class
class Calculator :IReciever
{
int x_;
int y_;
ACTIO_LIST currentAction;
public Calculator(int x, int y)
{
x_ = x;
y_ = y;
}
#region IReciever Members
public void SetAction(ACTIO_LIST action)
{
currentAction = action;
}
public int GetResult()
{
int result;
if (currentAction == ACTIO_LIST.ADD)
{
result = x_ + y_;
}
else if (currentAction == ACTIO_LIST.MULTIPLY)
{
result = x_ * y_;
}
else
{
result = x_ - y_;
}
return result;
}
#endregion
}
We will also be needing the class that will set let the user to
choose the command to execute i.e. the Client
(This
will be UI in our case). Finally we will be writting
an Invoker
, which will again be the user interface in our
case.
public partial class testForm : Form
{
IReciever calculator = null;
ACommand command = null;
AddCommand addCmd = null;
SubtractCommand subCmd = null;
MultiplyCommand mulCmd = null;
public testForm()
{
InitializeComponent();
}
private void testForm_Load(object sender, EventArgs e)
{
calculator = new Calculator(20, 10);
addCmd = new AddCommand(calculator);
subCmd = new SubtractCommand(calculator);
mulCmd = new MultiplyCommand(calculator);
}
private void radioAdd_CheckedChanged(object sender, EventArgs e)
{
if (radioAdd.Checked == true)
{
command = addCmd;
}
else if(radioSub.Checked == true)
{
command = subCmd;
}
else if (radioMultiply.Checked == true)
{
command = mulCmd;
}
}
private void button1_Click(object sender, EventArgs e)
{
label3.Text = "Result: " + command.Execute().ToString();
}
}
The event change of radio buttons are simply changing the command object
to be used and whe the user chooses to get
the result the appropiate command object will
be used to fetch the result for him. Befor wrapping up lets try to
look at the clas diagram of our code and map it with the Gofs diagram.
Points of Interest
Command pattern is a very good way of decrease the coupling between sender
and reciever. The most important thing
to remember while implementing the command pattern is that
the Command is just a link between sender and reciever.
It should only tell the reciever what the sender is expecting.
It should never alter the logic of sender and
reciever in any way.
History
01 March 2012: Understanding and Implementing Command Pattern in C#