I'm sure most of us are familiar with at least some of the patterns laid out by the "Gang of Four" in the book Design Patterns: Elements of Reusable Object-Oriented Software
. These patterns were incredibly helpful in OO languages because they provided answers to problems that these languages desperately needed.
OO languages were built around the idea of classes instead of functions, so while objects were often treated as first class citizens, functions were not. This inability to manipulate functions to their fullest extent left gaps in expressiveness that these patterns filled.
As time moves on, however, languages are learning from each other, and a lot of functional practices are moving to OO languages. As this happens, the patterns that answered these gaps in expression will no longer be needed.
In this series, I'll be going over patterns that are either already obsolete or are in the process of becoming so.
Command Pattern
The goal of this pattern was to encapsulate information about how to execute a method in a Command
. A Command
is an interface with a single abstract method usually called execute
of type () -> ()
. This method actually runs the method the Command is "wrapping".
The benefits of Commands were:
- The ability to be passed around as a first class value, e.g storing it in lists, passing it to other functions for execution based on certain conditions, etc.
- The separation of passing in parameters (specifically into the constructor) and execution of the function.
- The ability to group commands based on a common interface.
- The encapsulation of information about executing a function to reduce repetition at call sites, and the separation of business logic from other concerns like presentation or data storage.
However, all these benefits can be accomplished via other means. Not only this, but these other means are often more semantically expressive.
First, we'll go over an example of implementing the command pattern in Java 7. This example is mostly from GeeksforGeeks: Command Pattern. A similar example also exists on Wikipedia: Command Pattern. Afterwards, we'll refactor out the command pattern using Java 8 features.
Java 7 Code
Here are our Entities
:
package Entities;
import Boundary.IO;
public class Light {
private final IO io;
public Light(IO io) {
this.io = io;
}
public void on() {
io.show("Light is on");
}
public void off() {
io.show("Light is off");
}
}
package Entities;
import Commands.Command;
public class RemoteControl {
private Command button;
public RemoteControl() { }
public void setCommand(Command command) {
button = command;
}
public void pressButton() {
button.execute();
}
}
package Entities;
import Boundary.IO;
public class Stereo {
private final IO io;
public Stereo(IO io) {
this.io = io;
}
public void on() {
io.show("Stereo is on");
}
public void off() {
io.show("Stereo is off");
}
public void setCD() {
io.show("Stereo is set for CD input");
}
public void setVolume(int volume) {
io.show("Stereo volume set to " + volume);
}
}
Looking at the above, you might be curious what IO
is. Well, it's just a simple dependency that I defined in Boundary
so as to not couple my code with PrintStream
or more specifically System.out
.
The code is actually relatively simple:
package Boundary;
public interface IO {
void show(String str);
}
and the only implementation that exists in this app is this one:
package Presentation;
import Boundary.IO;
class ConsoleIO implements IO {
@Override
public void show(String str) {
System.out.println(str);
}
}
And finally, our Commands
:
package Commands;
public interface Command {
void execute();
}
package Commands;
import Entities.Light;
public class LightOnCommand implements Command {
private final Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
}
package Commands;
import Entities.Light;
public class LightOffCommand implements Command {
private final Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.off();
}
}
package Commands;
import Entities.Stereo;
public class StereoOnWithCDCommand implements Command {
private final Stereo stereo;
public StereoOnWithCDCommand(Stereo stereo) {
this.stereo = stereo;
}
public void execute() {
stereo.on();
stereo.setCD();
stereo.setVolume(10);
}
}
package Commands;
import Entities.Stereo;
public class StereoOffCommand implements Command {
private final Stereo stereo;
public StereoOffCommand(Stereo stereo) {
this.stereo = stereo;
}
public void execute() {
stereo.off();
}
}
This is the code we can use to see the above functionality:
import Boundary.IO;
import Commands.*;
import Entities.*;
import Presentation.ConsoleIO;
class Main {
public static void main(String[] args) {
RemoteControl remote = new RemoteControl();
IO io = new ConsoleIO();
Light light = new Light(io);
Stereo stereo = new Stereo(io);
remote.setCommand(new LightOnCommand(light));
remote.pressButton();
remote.setCommand(new StereoOnWithCDCommand(stereo));
remote.pressButton();
remote.setCommand(new StereoOffCommand(stereo));
remote.pressButton();
}
}
Java 7 Explanation
So far so good, but there's definitely a lot of overhead here. There is noticeable repetition, and that repetition impedes on the expressiveness of the code.
The main form of repetition is in this kind of code (across all the Command
s):
public class SomeClass implements Command {
private final SomeType someType;
public SomeClass(SomeType someType) {
this.someType = someType;
}
public void execute() {
someType.doStuff();
// sometimes more calls on `someType` are here
}
}
I like to call this kind of class a glorified function. The function would be typed as SomeType -> ()
, i.e it takes a SomeType and returns nothing.
It's "glorified" in the context of Java because it has some capabilities that normal functions do not have. You have the ability to pass in the parameter at a different time from execution, and pass around this partially applied function as a first class value.
In this case, the parameter being passed into the constructor of any Command is simply the corresponding entity (receiver) and nothing else. This actually means in our rewrite, we won't have to worry about higher order functions, because that entity is implicitly passed around to instance methods of said entity.
Before we dive more into the details of the rewrite though, lets actually look at the Java 8 code.
Java 8 Code
Entities:
// Identical to Java 7 version
package Entities;
import Boundary.IO;
public class Light {
private final IO io;
public Light(IO io) {
this.io = io;
}
public void on() {
io.show("Light is on");
}
public void off() {
io.show("Light is off");
}
}
// This uses Runnable instead of Command now
package Entities;
public class RemoteControl {
private Runnable button;
public RemoteControl() { }
public void setCommand(Runnable command) {
button = command;
}
public void pressButton() {
button.execute();
}
}
/*
We added a new method here (onWithCD) to replace one of the commands
that used multiple methods on this entity.
Then we made `off` public because that was just a 1:1 call
from a command previously.
And lastly we made everything else private. So although we
added code to this class, we actually reduced the public API.
*/
package Entities
import Boundary.IO;
public class Stereo {
private final IO io;
public Stereo(IO io) {
this.io = io;
}
public void onWithCD() {
on();
setCD();
setVolume(10);
}
public void off() {
io.show("Stereo is off");
}
private void on() {
io.show("Stereo is on");
}
private void setCD() {
io.show("Stereo is set for CD input");
}
private void setVolume(int volume) {
io.show("Stereo volume set to " + volume);
}
private void setDVD() {
io.show("Stereo is set for DVD input");
}
private void setRadio() {
io.show("Stereo is set for Radio");
}
}
IO:
// Identical to Java 7 version
package Boundary;
public interface IO {
void show(String str);
}
// Identical to Java 7 version
package Presentation;
import Boundary.IO;
class ConsoleIO implements IO {
@Override
public void show(String str) {
System.out.println(str);
}
}
Main:
// The method references used here instead of commands are talked about
// in the explanation below
import Boundary.IO;
import Entities.*;
import Presentation.ConsoleIO;
public class Main {
public static void main(String[] args) {
RemoteControl remote = new RemoteControl();
IO io = new ConsoleIO();
Light light = new Light(io);
Stereo stereo = new Stereo(io);
remote.setCommand(light::on);
remote.pressButton();
remote.setCommand(stereo::onWithCD);
remote.pressButton();
remote.setCommand(stereo::off);
remote.pressButton();
}
}
Java 8 Explanation
So while Stereo
is a tad longer, you probably noticed that an entire, relatively large, section is missing! Commands
are entirely gone, yet we still have code that hits all the original benefits enumerated at the top of this post. I'll go over those one by one, and explain how we're still achieving the benefits of the Command pattern without using Commands at all.
1
. The ability to be passed around as a first class value, e.g storing it in lists, passing it to other functions for execution based on certain conditions, etc.
So while Java lacks first class functions, it does have a Function
class, as well as other similar Functional Interfaces that can be implemented by lambdas or method references. And this Functional Interface is first class. When we construct a Runnable
with light::on
, we're passing a reference of that method to setCommand
, just like how we passed a reference of the various Command
s in Java 7.
2
. The separation of passing in parameters (specifically into the constructor) and execution of the function.
This is actually something I alluded to earlier, and we didn't do this specifically because we didn't need to here. The only thing we were passing into the Command
constructors were the entities they were accessing (only one for any given Command
).
This is easily possible though. If we needed parameters to be passed in at the time of setCommand
, we could do it by currying the functions that required the parameters (i.e returning a possibly nested Functional Interface from a method like onWithCD
or off
).
As a super simple example of currying, here's curried addition, note that the type is complex but the implementation is simple, so this is something more type inference could simplify in the future:
Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;
3
. The ability to group commands based on a common interface, usually literally calledCommand
.
This common interface in Java 8 is those FunctionalInterface
s (Runnable, Function, BiFunction, Predicate, etc.). The interface with the same type is Runnable
, which is why we used it in the Java 8 version.
In languages with first class functions, it's simply functions.
4
. The encapsulation of information about executing a function to reduce repetition at call sites, and the separation of business logic from other concerns like presentation or data storage.
The repetition is still reduced at the call site, because we still have the logic for that command that exists inside of a method. The separation is also achieved, because the business logic still lies with the entity and not inside the presentation layer.
As a bonus, the caller in our presentation layer won't be tempted to use the methods that used to be public on the entities before, because those are private now. The only reason they were public before was because the Command
needed to access them to expose them to the presentation layer.
Conclusion
So that's how you can get all the benefits of the Commands in newer languages without being dragged down by the semantic limitations of the Command Pattern.
An important thing to note is that this function-based approach is simpler in the very first version this was possible in Java (Java 8). Java is an OO language first and foremost. It was designed with classes as the foundation for all interactions. This language and others like it are why these kinds of patterns were necessary in the first place, and as these languages adopt certain aspects of functional languages, these OO patterns will become obsolete due to functions.
In a language that was designed from the ground up to use first-class functions, whether that be a functional language like Haskell or even another language in the JVM like Kotlin, this code is vastly simpler, especially with more advanced concepts like currying, higher order functions, etc.
Stay tuned for more parts in this series! Thanks for reading, and let me know if you have any patterns in mind that you want me to discuss!
Top comments (1)
Thanks for the article. I think you are calling the wrong function in RemoteControl class.
It should be