Design Patterns - The Command Pattern

There's a lot of knowledge in the book Design Patterns (Gang of Four).  Developers should know the material in there to write efficient and maintainable code, but the book is pretty dry.  I'd say it's quicker and easier to just point curious folks at the Wikipedia article, which sums up more information more quickly, and allows you to dive in if you find one particular bit interesting.

The other day, I was explaining a possible cleaner solution for very nested if/then/else statements; the Command Pattern.  In one variant of this pattern, you represent each "if" choice as an object, and you have a list of smaller objects instead of five hundred lines of hard-to-read/hard-to-maintain code.

Create an interface to tell us what a choice does:

public interface Choice {
    public boolean test(int value);
    public void action(int value);
}

Create a package to put choices into, and create choices that fulfill the interface:
public class LowRangeChoice implements Choice {
    public boolean test(int value) {
        return (value > 10);
    }
    public void action(int value) {
        // Do some action that would be *inside* of the if statement. 
     }
}

Create a List of choices in the constructor of the class with the large if/then/else block:
    List choices = new ArrayList();
    choices.add(new LowRangeChoice());
    ...

And finally, replace the if/then/else block with a short piece of code:
    for (Choice choice : choices) {
        if (choice.test(value)) { 
            choice.action(value);
         }
     }

On something relatively short, this would be overkill and would create a maintenance problem by itself.  However, on something really long - 2000+ lines, for example - this might make an unreadable class into a well ordered selection of small chunks, which seems ideal.

The Command Pattern is also good for quite a few more tasks than just this; a few good examples, mostly pulled from Wikipedia:
  • represent menu items, with each menu item having a test to see if it's been clicked and have an action associated to followup with.
  • represent every user action in the system, and keep previously selected actions on a stack, so that "undo" becomes much easier.
  • if you give a timeRemaining() method to each command, background tasks in the future can be estimated to show a running progress to the user while they wait.

I was going to add into this "how to determine a class is too long", but that seems topic enough for a second post.

No comments: