meeting -*- Outline -*- * Command (design patterns book, p. 233ff, Larman section 34.17) The basic idea of this pattern is to encapsulate a request as an object, this supports queuing and logging request, and also undo/redo, which is important for transactions... ** motivation Atomicity: a transaction is a set of tasks that must all complete successfully, or none of them must complete. For databases, one may want to reordered the changes to the database to improve performance; this implies sorting the tasks before executing them. To support crash recovery, one: - writes the series of changes to be made to the database to a log file, - makes the changes, - then writes a commit record to the log file Q: What happens if the system crashes during this operation? ** problem ------------------------------------------ COMMAND PATTERN (34.17) Problem: How to: - create, queue, and execute requests at different times, - support undo, - support logging to reapply changes after a crash - parameterized objects by an action to perform (e.g., menus) Solution: make each task a class that implements a common interface. ------------------------------------------ ** example ------------------------------------------ import java.util.*; public Transaction { private List commands; public void addDelete(PersistentObject o) { commands.add(new DBDeleteCommand(o); } public void addInsert(PersistentObject o) { commands.add(new DBInsertCommand(o); } public void addUpdate(PersistentObject o) { commands.add(new DBUpdateCommand(o); } public void commit() { sort(); Iterator i = commands.iterator(); while (i.hasNext()) { (ICommand)(i.next()).execute(); } } public void sort() { // ... } } ------------------------------------------ ------------------------------------------ public interface ICommand { void execute(); void undo(); } public abstract class DBCommand implements ICommand { protected PersistentObject object; } public class DBUpdateCommand extends DBCommand { DBUpdateCommand(PersistentObject o) { po = o; } public execute() { po.commit(); } } public class DBInsertCommand extends DBCommand { DBInsertCommand(PersistentObject o) { po = o; } public execute() { po.commit(); } } public class DBDeleteCommand extends DBCommand { DBDeleteCommand(PersistentObject o) { po = o; } public execute() { po.commit(); } } ------------------------------------------ ** discussion *** execution show sequence diagram: PersistentObject Transaction DBCommand | | create(o) | |---------------------------->| | | | | commit() | | | ---------->| | | | execute() | | |---------------------------->| | commit() | | | <---------------------|-----------------------------| | | | *** other examples A classic example of the use of the command pattern is for graphical user interfaces. A menu item is parameterized by a command object, it simply calls execute when pressed This supports actions, such as cut-and-paste, which have to be undone. useful in interpreters (the instructions are the commands) *** consequences - decouples the objects that invokes an operation from the one that knows how to perform it - you can assemble commands into composites to support things like macros - it's easy to add new commands *** implementation In C++, for commands that are not undoable and don't require arguments, you can use C++ templates to prevent creating a command class for every kind of action and receiver. See the design patterns book. *** related patterns a Composite can be used to implement macro commands a Momento can keep state the command requires to undo its effect a command that must be copied before being placed on a history list acts as a Prototype