免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1081 | 回复: 0
打印 上一主题 下一主题

Design Patterns: Command Pattern [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-10-05 09:35 |只看该作者 |倒序浏览
Author
Chris Lasater
Title
Design Patterns
Publisher
Wordware Publishing Inc.
Published
Coming soon!
ISBN
1-59822-031-4
Price
USD 39.95
Pages
296
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.

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:
//receiver
class Document
{
A collection object stores each line of text:
    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:
    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:
    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.

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:
//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:
//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:
    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:
//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:
    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:
    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:
    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:
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:
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:
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:
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:
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

About Christopher G. Lasater

Christopher G. Lasater
See my Website for Updates to books, articles and more...
I am also a published author, please check out my book:
ISBN: 1-59822-031-4
Title: Design Patterns
Author:Christopher G. Lasater
Wordware Publishing
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.
Please see his
blog
for article, product and discussion information.Click
here
to view Christopher G. Lasater's online profile.
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/12811/showart_394900.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP