The Command Pattern

The Command Pattern is useful to implement an instruction to perform a particular action. It contains and encapsulates all the information necessary for the action to be taken. It can allow to create composite commands, to keep track of a sequence of commands, and possibly to undo one of the commands. It can be used in GUI or transactions for example.

Think of a text editor. We can type characters and format them. Each command will implement an interface:

public interface Command {
   void perform();
   void undo();
}

public class Write implements Command {

   private TextEditor editor; // we will assume this class exists.
   private String string;
   private boolean success;

   public Write(TextEditor editor, String string) {
      this.editor = editor;
      this.string = string;
      this.success = false; // by default, a boolean is false, but it does not hurt to make it explicit!
   }

   @Override
   public void perform() {
       editor.write(string);
       success = true;
   }

   @Override
   public void undo() {
      if (success) { // we do not want to undo if the operation failed
        editor.erase(string); // more complex in reality, but not the purpose of this example.
      }
   }
}

public class Formatter implements Command {

  public enum Format {
     BOLD,
     ITALICIZE
  }

   private TextEditor editor;
   private Format format;
   private int startPosition; 
   private int endPosition;
   private boolean success;

   public Formatter(TextEditor editor, Format format, int startPosition, int endPosition) { // could chain with Write.. Chain of responsibility pattern!
      this.editor = editor;
      this.format = format;
      this.startPosition = startPosition;
      this.endPosition = endPosition;
      this.success = false;
   }

   @Override
   public void perform() {
      switch (format) {
         case BOLD:
           success = editor.bold(startPosition, endPosition);
           break;
         case ITALICIZE:
           success = editor.italicize(startPosition, endPosition);
           break;
         default: 
           break;
      }
   }

   @Override
   public void undo() {
      if (!success) return; // we do not want to undo if the operation failed
      switch (format) {
        case BOLD:
          editor.unbold(startPosition, endPosition);
          break;
        case ITALICIZE:
          editor.unbold(startPosition, endPosition);
          break;
        default:
          break;
      }
   }
}

The client code can chain commands or undo, etc.

TextEditor editor = new TextEditor();
Command awesome = new Write(editor, "This is awesome!");
Command author = new Write(editor, "By John Doe");
Command formatAwesome = new Formatter(editor, Formatter.Format.BOLD, 8, 15); 
Command formatAuthor = new Formatter(editor, Formatter.Format.ITALICIZE, 16, 27);

List<Command> cmds = new ArrayList<>();
cmds.add(awesome);
cmds.add(formatAwesome);
cmds.add(author);
cmds.add(formatAuthor);

try {
  cmds.forEach(Command::perform); // run
} catch (Exception e) {
  Collections.reverse(cmds); // reverse the command sequence.
  cmds.forEach(Command::undo); // will undo only those that succeeded.
}

This pattern can be combined with the Chain of Responsibility pattern or the Strategy pattern.

This is awesome! But is it really useful you could ask? What about just calling TextEditor’s methods? True! I was wondering the same. What is really powerful is how we can manage those commands. It can be more complex commands, sequenced, and we can create rollbacks, etc.

Author: Toujon Lyfoung

This paragraph is supposed to be the place where I put my credentials and achievements. In my opinion, degrees and jobs do not tell much about a person. If you want to know me, read my posts! Blogging has been fun. I do not pretend to do much. I am simply processing, tracking and sharing my reflection. Comments are definitely welcomed to help me continue in my learning.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s