Design Patterns in XML Applications
by Fabio Arciniegas A.
|
Pages: 1, 2, 3, 4, 5, 6
Synopsis
|
Contents |
|
Design Patterns in XML Applications |
Command is a behavioral pattern used to encapsulate actions in objects. This is highly useful when you want to keep track of changes made to a model, for example in supporting multi-level "do/undo."
Structure
The following is a class diagram of the command pattern. Slightly different versions of this pattern can be found in the literature, however, I chose to present it in this fashion for clarity.
|
| Figure 1. Command Pattern Structure |
XML Context
Suppose you are building an application that uses the DOM representation of an XML document as its basic datasay a component for displaying vector graphics, or a simple shopping list manager.
The user of your program will perform many operations, like deletions and additions. Since you are using the DOM as your underlying model, these changes will sooner or later translate into calls to removeChild and other DOM-specific calls. However, depending on how you structure your program, these changes can become either a hard-to-maintain, hard-to-extend mess, or an organized, extensible solution. Here is where the command pattern can help.
Let's take the shopping list editor as an example. The user wants to delete, add, and annotate the shopping list, among other operations. You use a GUI, so one option would be to hard-code your menu widgets' member calls to DOM-specific methods. For example, when the user selects the menu item "Insert," call insertChild. This has a number of "advantages":
-
Such code is fast to write.
-
Most GUI builders will "lead" you towards this.
-
It can be soft in terms of resource consumption.
It seems like it could be a real choice, but now you want to add undo/redo support to your program, and serious problems regarding this option become apparent:
-
There seems no easy way to maintain your do/undo list: either you change all your hardcoded widget events to call both the DOM methods and log to some list, or you change your DOM representation to somehow log the changes performed (!)
-
Even if you managed to successfully implement the do/undo lists from the hardcoded widget calls, you would be replicating that logic many times, which is hard to maintain and error-prone.
-
There is no clear indication as to which part of your program will manage the undo logic and how it will do it.
The solution that the command pattern proposes is to encapsulate the changes to the DOM into objects, command objects, each capable of doing (and undoing) a particular action. The collection of command objects will be managed by a certain command manager, capable of holding the queue of executed commands, so the user may undo/redo them.
Example
This example reflects a very common approach to DOM processing using the command pattern. If you will be writing applications using DOM as the underlying data structure representation, you are very likely to find this approach useful.
|
| Figure 2. Command Pattern Structure |
The figure shows the structure of a typical DOM-oriented application using the command pattern for its message passing. The following is the header file for the base class AbstractCommand, which is the foundation of the example. Please refer to command.zip for the complete example code.
|
| Figure 3. Command Example Header |
#include "heqetDef.h"
#include "Notification.h"
/**
AbstractCommand is the base class for all commands.
It provides do/undo operations as well as getDescription and
getState operations for the easy tracking of the executed commands.
(quite useful when keeping a menu of last performed operations).
*/
class AbstractCommand
{
public:
/**@name Comparison operators
* The comparison operators in the base AbstractCommand are
* provided in order to keep STL usability in the CommandManager
*/
//@{
/// equality operator
virtual int operator==();
/// unequality operator
virtual int operator!=();
/// increment operator
virtual void operator++();
//@}
/**@name Do / Undo methods
*/
//@{
/// Pure virtual operation that in child classes encapsulates the
logic of the change
virtual Notification do() = 0;
/// Pure virtual operation that in child classes encapsulates the
logic of undoing a change
virtual Notification undo() = 0;
/** Pure virtual operation that in child classes returns the description
of the operation
* (particularly useful for undo/redo lists presented to the user)
*/
virtual string getDescription() = 0;
//@}
};