What does the author mean by undoable operations? Hard to make out what he actually wants to refer here. I would appreciate a clarification.
He means just rolling back changes. I'm assuming you understand undos/redos and how they work to users just by virtue of using software in general (ex: ctrl-Z to undo a mistake you made). I might be wrong but I interpreted the question as more about how the command pattern facilitates undoing.
With the command pattern you have this object and abstraction between the invoker who wants to "do something" and the receiver who directly does it. As a basic example you might have an image software where the user wants to blur an image.
With the command pattern you'd have this sort of "Blur Image" command object with an abstract interface that all commands have in common, and so you have this command object to execute between the code that wants to blur an image and the code that actually does it. And that provides breathing room in the middle of these two to centralize functionality like logging all commands that are executed.
And likewise if the abstract command interface provides methods like undo/redo
, then specific command subtypes can implement/override those functions to undo and redo whatever changes they make in their execution. Then instead of discarding the command object, the system can push it to the history ("undo stack") and start invoking those undo/redo methods as the user moves around in the history, e.g.
With that aside, you don't actually have to have undo/redo
functions in the command interface to get undo/redo functionality in ways that still benefit from the command pattern (I wanted to pitch in primarily to mention this point). Again you have this central command execution area between invoker and receiver, so alternatively you can capture your application state before execution of any command (which would alter that state) and push that data (program state) to the undo stack (perhaps in a compacted form) prior to executing any command, and that likewise facilitates undoing/redoing even though the commands themselves don't provide undo/redo
functionality.
You have this sort "sandwich". If the invoker and receiver are like two pieces of bread, the command pattern gives you room to put all kinds of stuff in the middle of requesting to perform some operation, like centralized logging, later you might want to uniformly execute commands in a deferred async fashion, you might want to capture program state prior to execution of any command for undoing, you might want to execute the command in a separate process so that if it crashes, the host application doesn't crash, etc. etc. etc. Whereas if the invoker directly invoked functions in the receiver (ex: blurring images directly with no command in the middle), you don't have that centralized breathing room in the middle to uniformly add and do whatever you want.
And specifically by virtue of commands being abstract objects which encapsulate their execution parameters, you can keep them around and execute them at a later time, re-execute them, etc, long after the receiver made the original request. You can also serialize them cheaply. If your program state is uniformly altered by commands, then that state might span gigabytes but the entire user's command history might be capable of being stored in kilobytes, and you can reevaluate those commands and get back to exactly the same state the user was in by just executing those same commands again with the same parameters and order the user originally invoked them.