3

In a document editor application, is it better to implement the command pattern as a layer on top of the object model, or to deeply integrate it into the object model?

If commands are a layer on top, they would manipulate the object model using its regular getters and setters.

  • Pros: Commands are higher level, representing user actions that may involve various low-level model mutations. The model doesn't have to know about commands.
  • Cons: Object model mutators should have as little side effects as possible so that the commands know how to undo their effects.

If commands are deeply integrated, every object model mutator would automatically record an undo action for their effect.

  • Pros: Easier to guarantee that every mutation is correctly undoable.
  • Cons: Commands are lower-level than user actions, so there needs to be a begin/end signal to aggregate any model mutation into the current user-level action for undoing.

Or maybe a third option is to use the observer pattern to listen for low-level model mutations like property changes and record them into undoable commands, aggregated by user action?

Most command pattern articles online don't mention these architectural considerations. I'm looking for real-life experiences, open source software examples that support one of these approaches or articles/books that deal with the topic more in-depth.

Trillian
  • 141

1 Answers1

3

I've tried variations of all the options you laid out and I've had the most success with the third option. If you can decompose all model updates into a set of well-defined primitives (property changes and collection changes typically cover the majority of these updates), then you effectively get undo/redo for free without having to implement custom logic for each command.

The other benefit of this approach is the decoupling of commands and the changes they produce, which are typically coupled in the textbook command pattern. I think of "commands" as UI actions that belong in the application layer: they receive the active document as input, and might obtain additional input from the user before updating the model. The model emits a set of "changes" in response, which by themselves are another form of the traditional command pattern in the sense that they encapsulate undo/redo behavior on a model object.

As for real-world examples, IDEs have some of the most flexible command frameworks out there and most of them are open source. Have a look at the Eclipse command APIs or the command handlers in Roslyn (which provides most of the .NET functionality in Visual Studio). These platforms typically follow a similar approach where all changes are modeled as simple updates to an AST or a text buffer.

casablanca
  • 5,014