Click here to Skip to main content
15,867,756 members
Articles / Programming Languages / C#
Article

Design Patterns: Command Pattern

Rate me:
Please Sign up or sign in to vote.
4.59/5 (28 votes)
16 May 20076 min read 153.2K   119   5
Commands are useful tools when dealing with behaviors in objects. By making the request to an object, a Command object, and storing the command in an Invoker object, we can modify and keep historical records of different actions performed on an object.

book.jpg

AuthorChris Lasater
TitleDesign Patterns
PublisherWordware Publishing Inc.
PublishedComing soon!
ISBN 1-59822-031-4
PriceUSD 39.95
Pages296

Wordware is offering a 35% discount and free freight (continental U.S. only) to all Codeproject members/visitors when the book is purchased from www.wordware.com. The discount is good not only for my book, but for all books purchased from the site. Just enter the coupon code dp0314 when ordering.

What is a Command Pattern?

A Command Pattern allows requests to an object to exist as an object. What does that mean? It means that if you send a request for some function to an object, the command object can house that request inside the object. This is useful in the case of undoing or redoing some action, or simply storing an action in a request queue on an object. When you send the request, it is stored in the object. Then later, if you need to access that same request, or apply the request or some method on the request to an object, you can use the request object instead of calling the object's method directly.

The Command pattern has three main components: the Invoker, the Command, and the Receiver. The invoker component acts as a link between the commands and the receiver, and houses the receiver and the individual commands as they are sent. The command is an object that encapsulates a request to the receiver. The receiver is the component that is acted upon by each request.

Image 2

Figure 3-17. UML for command pattern

Let's take a look at an example of this interesting pattern. The example is a way to perform changes and undo those changes to text in a document. It shows how to use the command as a request to add some words to the document and store that text request.

Problem: A Document object needs a way to add and store undo and redo actions to its text

For our example, we will take a typical problem you may have encountered when using a simple text application like Notepad. You add some text, then find you need to undo what you have added. Sometime later, you realize you actually wanted the text after all, and wish to add it back to the document. Most of the simple text applications available don't have an undo queue, or have one for only one action. In the example below, there is no such functionality. You realize that this would be a really useful feature to add, since your text document has no concept of history of changes made to its text. Your current Document object stores text as lines of strings within an ArrayList. When you remove the text it is gone, with no way to redo your previous text:

C#
//receiver
class Document
{

A collection object stores each line of text:

C#
private ArrayList _textArray = new ArrayList();

Methods exist for adding and removing lines of text. When one is added or removed, it is permanent, you cannot get it back if removed:

C#
public void Write(string text)
{
    _textArray.Add(text);
}
public void Erase(string text)
{
    _textArray.Remove(text);
}
public void Erase(int textLevel)
{
    _textArray.RemoveAt(textLevel);
}

There is a method to display all the lines of text in order. When called, this displays the current lines of text in the array list:

C#
    public string ReadDocument()
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        foreach(string text in _textArray)
            sb.Append(text);
        return sb.ToString();
    }
}

We need a way to introduce redo/undo functionality into our document object. In the solution, we will see how this pattern accomplishes just that by storing commands as requests for a document.

Solution: Use a command as the request to store the text, and allow the command to handle undo and redo of the document

To allow historical requests on our document and redo/undo functionality on those requests, we will use a Command class as a storage object for the request. Each command will house the text for the document, and methods to either undo or redo the text.

Image 3

Figure 3-18. UML for command pattern example

To give us the desired functionality, we need to first create a base abstract class: Command. This class will serve as a contract for the inherited command classes. We have two abstract methods, Redo and Undo. These methods are to be implemented in the concrete classes, and will contain references to methods on the Document object:

C#
//base command
abstract class Command
{        
    abstract public void Redo();
    abstract public void Undo();
}

Next, we take a look at our concrete command class. Here, we store the reference to the added text, and a reference to our document. The text is part of the request, and is how each request will modify the document:

C#
//concrete implementation
class DocumentEditCommand : Command
{
    private Document _editableDoc;
    private string _text;

    public DocumentEditCommand(Document doc, string text)
    {
        _editableDoc = doc;
        _text = text;
        _editableDoc.Write(_text);
    }

Each of the parent class's abstract methods is overridden and implemented here, giving us references to the document's methods to add and subtract lines of text:

C#
    override public void Redo()
    {
        _editableDoc.Write(_text);
    }
    override public void Undo()
    {
        _editableDoc.Erase(_text);
    }
}

Next, we look at the Invoker object. This object serves as a repository for all request objects for this particular document:

C#
//invoker
class DocumentInvoker
{
    private ArrayList _commands = new ArrayList();

We create and store a new document when the invoker instance is created. The invoker, then, can allow any command to access and modify the document's text:

C#
private Document _doc = new Document();

Which command is used on the document is based on the historical level, or number of the request in the queue:

C#
public void Redo( int level )
{
    Console.WriteLine( "---- Redo {0} level ", level );
    ((Command)_commands[ level ]).Redo();
}

public void Undo( int level )
{
    Console.WriteLine( "---- Undo {0} level ", level );
    ((Command)_commands[ level ]).Undo();
}

The document acts as the receiver of the action of the request, and the invoker is the container for all the actions. Below we see that the invoker class methods create and store commands, as well as apply them to the document:

C#
    public void Write(string text)
    {
        DocumentEditCommand cmd = new 
            DocumentEditCommand(_doc,text);
        _commands.Add(cmd);
    }
    
    public string Read()
    {
        return _doc.ReadDocument();
    }
}

Now, we will look at how we can use the invoker and command relationship to the document to perform undo and redo actions on the document. First, we need to write some text to the document:

C#
DocumentInvoker instance = new DocumentInvoker ();
instance.Write("This is the original text.");

Here is the text so far:

This is the original text.--first write

Now, we write another line into the DocumentInvoker instance:

C#
instance.Write(" Here is some other text.");
This is the original text. Here is some other text.--second write

Next, to illustrate the usefulness of the command, we perform an undo using the second command:

C#
instance.Undo(1);

Here is the text now. Notice that the text has returned to its original state before the second write:

---- Undo 1 level
This is the original text.

After that, we perform a redo with the same command. Notice this is possible because we store the text for the undo and redo within the command inside the invoker class:

C#
instance.Redo(1);

Here is the text now. The text has been re-written with the new text at the end:

---- Redo 1 level
This is the original text. Here is some other text.

We go on to perform undo and redo functions in a variety of operational orders to illustrate the flexible nature of the command pattern strategy:

C#
instance.Write(" And a little more text.");
instance.Undo(2);
instance.Redo(2);
instance.Undo(1);

And can see the results of our actions in the console window:

This is the original text. Here is some other text. And a little more text.
---- Undo 2 level
This is the original text. Here is some other text.
---- Redo 2 level
This is the original text. Here is some other text. And a little
more text.
---- Undo 1 level
This is the original text. And a little more text.

Comparison to similar patterns

Commands and Mementos have some similarity due to the fact they both work with an object's internal properties. The Command pattern keeps a record of changes to an object's state, and applies those changes in an ad-hoc fashion. A Memento also records changes to an object's state, and can restore that state at any time. The Chain of Responsibility pattern seems to handle processing in a similar manner to the Command, except it hands off processing to another process linearly unlike the Command pattern. An Interpreter pattern works in the example above because we are using language elements to determine which changes to apply at a given time.

What we have learned

Commands are useful tools when dealing with behaviors in objects. By making the request to an object, a Command object, and storing the command in an Invoker object, we can modify and keep historical records of different actions performed on an object. Virtually, any action could be stored as a command, and used to process requests in a variety of operational orders on a receiving object.

Related Patterns:

  • Composite Pattern
  • Memento Pattern
  • Interpreter Pattern
  • Chain of Responsibility Pattern

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Christopher G. Lasater

I am also a published author, please check out my book:
ISBN: 1-59822-031-4
Title: Design Patterns
Author:Christopher G. Lasater
More from my web site
Amazon.com


Comments and Discussions

 
SuggestionChanging ArrayList to List<Command> in Invoker class. Pin
Kishor Deshpande15-Jan-13 4:30
professionalKishor Deshpande15-Jan-13 4:30 
GeneralMy vote of 3 Pin
B.Farivar15-Sep-12 23:54
B.Farivar15-Sep-12 23:54 
GeneralMy vote of 2 Pin
Ted Wong15-Aug-10 18:49
Ted Wong15-Aug-10 18:49 
GeneralNo source for download Pin
Ted Wong15-Aug-10 18:48
Ted Wong15-Aug-10 18:48 
GeneralBook Discount from Wordware Pin
Christopher G. Lasater16-May-07 9:06
Christopher G. Lasater16-May-07 9:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.