Overview
Overview
Due to the current inclusion of Java 8 Functional Programming Paradigm in today’s world, people still wonder how it could collaborate well with the existing object oriented approach. That’s why I would love to demonstrate a concrete example of command pattern using Java 8 Lambda Expression.
Command Pattern

The Problem Domain
An editor or IDE has gallons of actions or commands such as open, close, save etc. Each and every time, someone needs to develop a new command for the IDE, it should conform to the Open-Closed principle - Open for Extension and Closed for Modification. So, now our design decision should be effective enough to allow extensions to the applications so that anyone can easily plug-in new command to the IDE.
OOP way
First of all, I would love to tell you about how it can be achieved using the object oriented way.
Command
public interface Command {
public void perform();
}
Concrete Command - Open
public final class Open implements Command {
private final Editor editor;
public Open(final Editor editor) {
super();
this.editor = editor;
}
@Override
public void perform() {
this.editor.open();
}
}
Concrete Command - Save
public final class Save implements Command {
private final Editor editor;
public Save(final Editor editor) {
super();
this.editor = editor;
}
@Override
public void perform() {
this.editor.save();
}
}
Receiver
public interface Editor {
public void close();
public void open();
public void save();
}
Concrete Receiver
public final class MockEditor implements Editor {
private final List<String> actions = Lists.newArrayList();
@Override
public void close() {
this.actions.add("close");
}
@Override
public void open() {
this.actions.add("open");
}
@Override
public void save() {
this.actions.add("save");
}
}
Invoker
public final class Macro {
private final List<Command> commands;
public Macro() {
this.commands = Lists.newArrayList();
}
public void record(final Command command) {
this.commands.add(command);
}
public void run() {
this.commands.forEach(Command::perform);
}
}
Client
public final class Client {
public static void main(final String[] args) {
final Macro macro = new Macro();
final Editor editor = new MockEditor();
final Command openCommand = new Open(editor);
final Command saveCommand = new Save(editor);
macro.record(openCommand);
macro.record(saveCommand);
macro.run();
}
}
Functional Way
So far, we have seen the object-oriented approach in solving our defined problem using Command Pattern. Now, we would try to solve it using Functional Programming Paradigm which I believe would be more concise and better solution.
Command
public interface Command {
public void perform();
}
Receiver
public interface Editor {
public void close();
public void open();
public void save();
}
Invoker
public final class Macro {
private final List<Command> commands;
public Macro() {
this.commands = Lists.newArrayList();
}
public void record(final Command command) {
this.commands.add(command);
}
public void run() {
this.commands.forEach(Command::perform);
}
}
Client
public final class Client {
public static void main(final String[] args) {
final Macro macro = new Macro();
final Editor editor = new MockEditor();
macro.record(() -> new Open(editor));
macro.record(() -> new Save(editor));
macro.run(); // 1st way
macro.record(() -> editor.open());
macro.record(() -> editor.save());
macro.run(); // 2nd way
macro.record(editor::open);
macro.record(editor::save);
macro.run(); // 3rd way
}
}
Benefits
You can clearly see that I didn’t write any concrete implementation class of the commands. I have only passed those implementations as behaviors to the record method as it accepts SAM (Single Abstract Method) Interface (Command).