This is the first in a series of articles where we will review many Gang of Four patterns under a functional light, showing how they can be re-implemented in more concise and often more flexible ways.

Before analysing in detail the different patterns, let me start in an unusual way for a programming post: a simple exercise of grammatical analysis. Consider a sentence like: “smoking is unhealthy” or even “running is tiring”. What are “smoking” and “running” in this context? In English, the -ing suffix transforms verbs like to smoke or to run into nouns. The biggest part of the design patterns listed in the Gang of Four book, especially the ones classified as behavioural patterns, follow exactly the same approach. Like the -ing suffix, they turn verbs into nouns – or in this case, functions into objects.

Unfortunately, this transformation process is often unnecessary, or merely serves the purpose of shoehorning some concepts that are natural in functional programming into the object oriented paradigm. Moreover, this adaptation comes at the cost of a higher verbosity, lower readability and more difficult maintainability. In fact, it not only requires you to create objects with the exclusive purpose of wrapping one or more functions into them, but it also makes it necessary to develop some extra logic to glue these objects together down the line. The same goal could be achieved with a straightforward function composition.

Let’s begin unpicking the different patterns beginning with one of the most common ones.

The Command Pattern

The Command pattern is one of the patterns where this functions to object translation process is more evident (together with its redundancy. Let’s see a simple example of how this works. First of all it is necessary to define an interface modelling a command:

interface Command {
    void run();
}

At this point, you can provide different implementations of this Command interface. For instance, let’s suppose we want different commands to handle a message – you could have one that logs it:

public class Logger implements Command {
    public final String message;

    public Logger( String message ) {
        this.message = message;
    }

    @Override
    public void run() {
        System.out.println("Logging: " + message);
    }
}

… one that saves the message on a file:

public class FileSaver implements Command {
    public final String message;

    public FileSaver( String message ) {
        this.message = message;
    }

    @Override
    public void run() {
        System.out.println("Saving: " + message);
    }
}

and one that sends it via email:

public class Mailer implements Command {
    public final String message;

    public Mailer( String message ) {
        this.message = message;
    }

    @Override
    public void run() {
        System.out.println("Sending: " + message);
    }
}

Now it is necessary to have an object that can execute one or more of this commands:

public class Executor {
    public void execute(List<Command> tasks) {
        for (Command task : tasks) {
            task.run();
        }
    }
}

and finally it is possible to add the commands that we want to run into a List and execute them through the Executor:

List<Command> tasks = new ArrayList<>();
tasks.add(new Logger( "Hi" ));
tasks.add(new FileSaver( "Cheers" ));
tasks.add(new Mailer( "Bye" ));

new Executor().execute( tasks );

As expected, the Gang of Four book suggests we wrap functions (the actions to be executed) into objects (the commands executing those actions), however, this level of indirection doesn’t provide any advantage aside from allowing us to fit functions into a strictly object oriented programming style. However, with the introduction of lambdas in Java 8, it is now possible to seamlessly mix both the functional and the object oriented paradigms we can rethink the former example in a more compact way.

First of all, notice that we don’t need to define the Command interface: the Runnable one, which exists from the very first version of Java, has a single abstract method with the same signature of the Command one. The former 3 commands implementation can be replaced in a more concise way by 3 functions that in Java 8 can be implemented with 3 static methods:

public static void log(String message) {
    System.out.println("Logging: " + message);
}

public static void save(String message) {
    System.out.println("Saving: " + message);
}

public static void send(String message) {
    System.out.println("Sending: " + message);
}

Rethinking the command implementation in terms of plain function brings the benefit of dramatically increase the signal / noise ratio of our code, where the signal is the body of the functions while the noise is all the additional code used to represent that function as the method of an object. Even the Executor class can be replaced by a one-line static method, taking a List of Runnables as argument:

public static void execute(List<Runnable> tasks ) {
    tasks.forEach( Runnable::run );
}

our functions can be added into a List and executed exactly as we did before.

List<Runnable> tasks = new ArrayList<>();
tasks.add(() -> log("Hi"));
tasks.add(() -> save("Cheers"));
tasks.add(() -> send("Bye"));

execute( tasks );

Here the Java compiler automatically translates the lambdas with no arguments and invoking the void static methods executing the actions formerly wrapped in commands into anonymous implementation of the Runnable interface thus allowing to collect them in a List of Runnables.

The Strategy Pattern

The intent of the Strategy pattern is generalising a procedure, requiring us to use different but interchangeable algorithms. Let’s put this pattern to work in another practical example. We want to generalise a procedure which takes a text in input, filters it using a given criteria and prints it on the standard output after having eventually formatted or transformed it. In other words, we need to generalise 2 behaviours: the one filtering the text, and the other transforming it. The first step is putting the abstract definition of these 2 behaviours into an interface.

interface TextFormatter {
    boolean filter(String text);
    String format(String text);
}

After this, it is possible to develop a class implementing a general procedure to publish a text which is passed with an instance of the TextFormatter interface (the strategy) that in turns encapsulates the details of how the user wants to filter and format the text.

public class TextEditor {
    private final TextFormatter textFormatter;

    public TextEditor(TextFormatter textFormatter) {
        this.textFormatter = textFormatter;
    }

    public void publishText(String text) {
       if (textFormatter.filter( text )) {
           System.out.println( textFormatter.format( text ) );
        }
    }
}

We can now provide multiple implementation of the strategy. The most obvious one accepts any text and prints it as it is.

public class PlainTextFormatter implements TextFormatter {

    @Override
    public boolean filter( String text ) {
        return true;
    }

    @Override
    public String format( String text ) {
        return text;
    }
}

Another one could be used to parse a log file line by line in search of error messages so it accepts only the sentences starting with “ERROR” and prints them in uppercase.

public class ErrorTextFormatter implements TextFormatter {

    @Override
    public boolean filter( String text ) {
        return text.startsWith( "ERROR" );
    }

    @Override
    public String format( String text ) {
        return text.toUpperCase();
    }
}

Finally, we could have a more imaginative that prints in lower case only the text that is shorter than 20 characters.

public class ShortTextFormatter implements TextFormatter {

    @Override
    public boolean filter( String text ) {
        return text.length() < 20;
    }

    @Override
    public String format( String text ) {
        return text.toLowerCase();
    }
}

At this point, we can create a TextEditor passing a TextFormatter instance that is specific for the type of text publishing that we want to perform.

TextEditor textEditor = new TextEditor( new ErrorTextFormatter() );
textEditor.publishText( "ERROR - something bad happened" );
textEditor.publishText( "DEBUG - I'm here" );

So far so good, but once again there is the sense that this implementation is more verbose than it could be. This time, the only relevant signal is the logic implemented in the publishText of the TextEditor class. The 2 behaviours defined by the TextFormatter interface can be passed to the publishText method using 2 functions: a Predicate to filter the test to be published, and a UnaryOperator (a function transforming an object in another object of the same type) to format it before sending it to the standard output.

public static void publishText( String text, Predicate<String> filter, UnaryOperator<String> format) {
    if (filter.test( text )) {
        System.out.println( format.apply( text ) );
    }
}

In this way we can publish any text as it is, as done by the PlainTextFormatter as it follows:

publishText( "DEBUG - I'm here", s -> true, s -> s );

or can reimplement the features provided by ErrorTextFormatter passing a Predicate which only accepts text starting with “ERROR” and a Function converting the String to upper case.

publishText( "ERROR - something bad happened", s -> s.startsWith( "ERROR" ), String::toUpperCase );

One common objection to this more compact solution is that explicitly implementing the TextFormatter in a class allows to have a library of strategies that can be reused instead of repeating their implementation for each single invocation. However, nothing prevents us to adopt a similar approach with functions and collect them in an opportune library like this.

public class TextUtil {
    public boolean acceptAll(String text) {
        return true;
    }

    public String noFormatting(String text) {
        return text;
    }

    public boolean acceptErrors(String text) {
        return text.startsWith( "ERROR" );
    }

    public String formatError(String text) {
        return text.toUpperCase();
    }
}

In this way, instead of using anonymous lambdas, we can reuse the functions defined there as in:

publishText( "DEBUG - I'm here", TextUtil::acceptAll, TextUtil::noFormatting );

or

publishText( "ERROR - something bad happened", TextUtil::acceptErrors, TextUtil::formatError );

It is worth noticing that the functions are actually finer grained than the strategy classes (they can be combined in a way not available by any class) and allow even better reusability. In the next part of this series, we will review 2 other very commonly used GoF patterns under a functional lens 2: the Template and the Observer patterns.