Using the command pattern with lambda expressions

Reduce boilerplate and make the intent of your code more obvious.

As Java developers we’re all familiar with the concept of Design Patterns. These are codified template solutions to problems that we may encounter when developing. We’re probably also familiar with being told by our functional programming brethren that design patterns are just “missing” language features. So the question now arises, with the introduction of lambda expressions in Java 8 and a more functional style of programming, how do design patterns change? In this article we’ll be looking at the command pattern.

A command object is an object that encapsulates all the information required to call another method later. The command pattern is a way of using this object in order to write generic code that sequences and executes methods based on runtime decisions.

There are four classes that take part in the command pattern:

  • Receiver – Performs the actual work
  • Command – Encapsulates all the information required to call the receiver
  • Invoker – Controls the sequencing and execution of one or more commands
  • Client – Creates concrete command instances

invoker

Let’s look at a concrete example of the command pattern and see how it improves with lambda expressions. Suppose we have a GUI Editor component that has actions upon it that we’ll be calling, such as open or save as shown below. We want to implement macro functionality—that is, a series of operations that can be recorded and then run later as a single operation. This is our receiver.

public interface Editor {
    public void save();
    public void open();
    public void close();
}

In this example, each of the operations, such as open and save, are commands. We need a generic command interface to fit these different operations into. I’ll call this interface Action, as it represents performing a single action within our domain. This is the interface that all our command objects implement.

public interface Action {
    public void perform();
}

We can now implement our Action interface for each of the operations. All these classes need to do is call a single method on our Editor and wrap this call into our Action interface. I’ll name the classes after the operations that they wrap, with the appropriate class naming convention—so, the save method corresponds to a class called Save.

public class Save implements Action {
    private final Editor editor;

    public Save(Editor editor) {
        this.editor = editor;
    }

    @Override
    public void perform() {
        editor.save();
    }
}

Now we can implement our Macro class. A macro consists of a sequence of actions that can be invoked in turn and this is our invoker. This class can record actions and run them as a group. We use a List to store the sequence of actions and then call forEach in order to execute each Action in turn.

public class Macro {
    private final List<Action> actions;

    public Macro() {
        actions = new ArrayList<>();
    }

    public void record(Action action) {
        actions.add(action);
    }

    public void run() {
        actions.forEach(Action::perform);
    }
}

When we come to build up a macro programmatically, we add an instance of each command that has been recorded to the Macro object. We can then just run the macro and it will call each of the commands in turn. As a lazy programmer, I love the ability to define common workflows as macros. Did I say “lazy”? I meant focused on improving my productivity. The Macro object is our client code.

Macro macro = new Macro();
macro.record(new Open(editor));
macro.record(new Save(editor));
macro.record(new Close(editor));
macro.run();

How do lambda expressions help? Actually, all our command classes, such as Save and Open , are really just lambda expressions wanting to get out of their shells. They are blocks of behavior that we’re creating classes in order to pass around. This whole pattern becomes a lot simpler with lambda expressions because we can entirely dispense with these classes. The following code shows how to use our Macro class without these command classes and with lambda expressions instead.

Macro macro = new Macro();
macro.record(() -> editor.open());
macro.record(() -> editor.save());
macro.record(() -> editor.close());
macro.run();

In fact, we can do this even better by recognizing that each of these lambda expressions is performing a single method call. So, we can actually use method references in order to wire the editor’s commands to the macro object.

Macro macro = new Macro();
macro.record(editor::open);
macro.record(editor::save);
macro.record(editor::close);
macro.run();

We’ve taken a look at the command pattern and seen how its expressed in Java 8. In some respects the command pattern is really just a poor man’s lambda expression to begin with. By using actual lambda expressions or method references, we can clean up the code, reducing the amount of boilerplate required and making the intent of the code more obvious. This is just one example of the broader trend when re-evaluating design patterns in the context of Java 8. Many of these patterns express a useful underlying idea that can continue to be of value to developers in the modern world, but new language idioms improve and clarify design patterns.

Editor’s note: If you’re interested in gaining a deeper understanding of Java 8 lambda expressions, check out the book Java 8 Lambdas by Richard Warburton. You can also explore his Java 8 training course at java8training.com.

tags: , , ,