Santiago Palladino

Santiago Palladino

Command pattern in Web Apps

7 min
Oct 14 2008
coding, web
7 min
Oct 14 2008

The command pattern is a behavioural pattern that encapsulates a request made to a certain object inside a Command object. Each type of command knows how to Execute() itself, as well as its target instances.

In the example above, taken from the Gang of Four's Design Patterns, a PasteCommand would be associated to the Paste menu item. When the item is clicked, the command is executed, and, having a reference to the Document, calls Paste() on it.

This pattern is useful when you want to defer the execution of a certain command, for example, or store a log for a given transaction to be able to recover from a crash. It also allows you to define a high-level language for operations within your system.

However, the motivation for it in this case is to be able to support undo/redo operations. This requires a number of additions to the standard Command pattern.

First of all, the abstract Command class must implement Undo() and Redo() methods, aside from Execute(). It will also probably need an old state object, in order to know how to go back to the previous state before its execution. Therefore, having OldState and NewState fields in each command is a good practice to give the command all the information it needs for execution. The Sender of the command mayalso be required.

To handle these commands, a global History object is necessary. This object has a collection of listeners that implement a certain interface (in the example, the Document), and methods to Execute a command, Undo and Redo.

The mechanics would be the following: at the beginning of the application all listeners subscribe to the History class. When an option is selected, a Command is created with current and next state, and sent to the history for execution (note that current state can be obtained from a State provider).

History then executes the command for each of its listeners, which will decide whether to take action based on the command type and parameters. And then it stores the command inside a list for undoing and redoing.

When the history receives an Undo message, it looks for the last executed command and calls its Undo() method. In order to support multiple undo operations, the history must keep a current command index.

Grouping commands requires special attention. Sometimes a single action may trigger more than one command (for example, clicking on a link may involve OpenBrowserCommand and NavigateToCommand). However, if the user undoes and redoes this action, the browser will first open in an empty browser and only navigate in the next redo, exposing the nature of the commands to the user.

In order to avoid this, one possibility is to have a MacroCommand object that contains multiple commands. Whenever it is executed, it invokes the Execute() method of all of its children, same as Undo() and Redo().

The problem with this is that a single action may involve several components. For example, when a browser is created, it may decide on its own to launch a NavigateToCommand. We can't group this with the previous command as it depends on its execution.

This is when timing commands comes in handy. By adding a timestamp to each command, the undo and redo operation can be done by timespans. Therefore, when the History receives an Undo message, we don't undo the last command, but the commands executed in the last second.

Dealing with time is always problematic. A command that takes too long to execute may exceed this threshold, and a fast-clicking user may trigger two different actions within the timespan, and be undone together. Tuning this threshold is more difficult and error prone than the MacroCommand alternative, yet much more flexible.

The usage of commands in a web application is also good for logging. Whenever a command is issued, logging its parameters and states allow you to keep a trace on the user actions. With appropriate analysis tools, it can allow you to extract metrics on actual user reactions upon the interface. Knowing that all users entering the "Configuration" page are leaving without altering its content could mean that the page is not very intuitive (if not scary).

It also allows you to inject different aspects into the system, such as providing help based on the user actions, which will hopefully be the subject of the next post.

Update: As Martin pointed out, GWT provides a History object that allows you to manage the browser's back and forward buttons, by storing states (commands) within the browser's history stack and responding to changes via listeners.