In the first article of this series, we started investigating how the most common GoF patterns can be reviewed and reimplemented in a cleaner and more concise way by leveraging the new functional features introduced with Java 8. In this second part, we will continue this process and revisit two other widely used GoF patterns: the Template and the Observer patterns, which can both be reimplemented through the Java 8 Consumer interface.

The Template pattern

To practically illustrate in which cases the Template pattern can be useful let’s suppose we have a Resource class like the following:

public class Resource {
    public Resource() {
        System.out.println("Resource created");
    }
    
    public void useResource() {
        riskyOperation();
        System.out.println("Resource used");
    }
   
    public void employResource() {
        riskyOperation();
        System.out.println("Resource employed");
    }
    
    public void dispose() {
        System.out.println("Resource disposed");
    }
    
    private void riskyOperation() {
        if ( new Random().nextInt( 10 ) == 0) {
            throw new RuntimeException();
        }
     }
 }

Our Resource has a constructor, some methods to access it and a further method to dispose it when it is no longer in use. These methods can do a risky operation like using a database or a network connection and then they may eventually fail throwing a RuntimeException. The possibility of a failure has been simulated here by randomly throwing an exception one time out of 10. Regardless of the outcome of these method invocations, calling dispose() after having finished to use the Resource is mandatory in order to release the connections and other used artifacts and thus avoiding a memory, connection of file pointer leak. In this case, using the Resource as follows…

Resource resource = new Resource();
resource.useResource();
resource.employResource();
resource.dispose();

…is wrong, because the useResoure() or employResource() methods may eventually throw an exception then preventing the correct disposal of the Resource. Clearly, the right way to use this Resource is like this:

Resource resource = new Resource();
try {
    resource.useResource();
    resource.employResource();
} finally {	
    resource.dispose();
}

The problem is that we cannot garantee that every time our Resource is used will be disposed correctly following this pattern. We don’t want to risk the possibility of the Resource being misused – or perhaps we would like to provide an API forcing the clients of the Resource object to always dispose it. In other words, we want to make sure that the clients of our Resource will always use it following a pattern such as this:

openResource();
try {
    doSomethingWithResource();
} finally {
   closeResource();
}

Here we just defined a code template and then we can put it in an abstract class:

public abstract class AbstractResourceManipulatorTemplate {
    protected Resource resource;

    private void openResource() {
        resource = new Resource();
    }

    protected abstract void doSomethingWithResource();

    private void closeResource() {
        resource.dispose();
        resource = null;
    }

    public void execute() {
        openResource();
        try {
            doSomethingWithResource();
        } finally {
            closeResource();
        }
    }
}

This abstract template encapsulates the usage pattern that we wanted to enforce and provides an abstract method where the different concrete implementations can define how to interact with the Resource.

public class ResourceUser extends AbstractResourceManipulatorTemplate {
    @Override
    protected void doSomethingWithResource() {
        resource.useResource();
    }
}
public class ResourceEmployer extends AbstractResourceManipulatorTemplate {
    @Override
    protected void doSomethingWithResource() {
        resource.employResource();
    }
}

In this way, calling execute() on these concrete implementations performs on the Resource the actions defined in the respective doSomethingWithResource() methods’ body, and at the same time, ensures that every time a Resource it’s used it will also be correctly disposed.

new ResourceUser().execute();
new ResourceEmployer().execute();

The idea of providing a template to enforce the clients of our Resource to use it in the right way is correct, but as we have already seen for other patterns, the pure OOP implementation described in the GoF book is verbose and cumbersome. There is a straightforward way to achieve the same result with a single method accepting a Consumer of the Resource.

public static void withResource( Consumer<Resource> consumer) {
    Resource resource = new Resource();
    try {
        consumer.accept( resource );
    } finally {
        resource.dispose();
    }
}

Note that the body of this method is equivalent to what done by the execute() method of the abstract template with the only difference that the degree of freedom provided by the template’s abstract method is now achieved through the Consumer passed to the withResouce() method. The concrete template implementation defining how the Resource has to be used can be then replaced by a simple lambda expression.

withResource( resource -> resource.useResource() );
withResource( resource -> resource.employResource() );

The Observer pattern

The Observer pattern is for sure one of the most common and widely used pattern. Its purpose is allowing one or more objects to to be notified when a certain event occurs and act accordingly. The main abstraction of this pattern is the Listener interface:

interface Listener {
    void onEvent(Object event);
}

When on object wants to be notified when the event happens, or to listen for the event, it has only to implement this interface and encode in the onEvent() method’s body how it wants to react to the arrival of an event. The counterpart of this is an Observable object or in other words an object that notifies its registered listeners by sending them events when something relevant happens.

public class Observable {
    private final Map<Object, Listener> listeners = new ConcurrentHashMap<>();

    public void register(Object key, Listener listener) {
        listeners.put(key, listener);
    }

    public void unregister(Object key) {
        listeners.remove(key);
    }

    public void sendEvent(Object event) {
        for (Listener listener : listeners.values()) {
            listener.onEvent( event );
        }
    }
}

Before the introduction of lambdas the 2 typical methods to register a Listener on this Observable were either through an anonymous inner class:

public class Observer1 {
    Observer1(Observable observable) {
        observable.register( this, new Listener() {
            @Override
            public void onEvent( Object event ) {
                System.out.println(event);
            }
        } );
    }
}

or making your object implement the Listener interface directly.

public class Observer2 implements Listener {
    Observer2(Observable observable) {
        observable.register( this, this );
    }
    @Override
    public void onEvent( Object event ) {
        System.out.println(event);
    }
}

Both these Observers can be used in the same way and when the Observable will send an event it will be broadcast to both:

Observable observable = new Observable();
new Observer1( observable );
new Observer2( observable );
observable.sendEvent( "Hello World!" );

However, once again, both these solutions reveal the common problem of the biggest part of GoF patterns: they oblige to transform verbs, the actions to be taken when an event arrives, into nouns, the classes, anonymous or not wrapping those actions. The first thing to be noticed in order to leverage the new functional features of Java 8 is that the Listener interface we defined above is semantically equivalent to the Consumer one and then we can get rid of the first and replace it with the second.

public class Observable {
    private final Map<Object, Consumer<Object>> listeners = new ConcurrentHashMap<>();

    public void register(Object key, Consumer<Object> listener) {
        listeners.put(key, listener);
    }

    public void unregister(Object key) {
        listeners.remove(key);
    }

    public void sendEvent(Object event) {
        listeners.values().forEach( listener -> listener.accept( event ) );
    }
}

Moreover, it is no longer necessary to implement the Listener with a specific class and the reaction to the event arrival can be encoded with a lambda expression or in this case also with a more concise method reference.

Observable observable = new Observable();
observable.register( "key1", e -> System.out.println(e) );
observable.register( "key2", System.out::println );
observable.sendEvent( "Hello World!" );

In the third article of this series, we will continue our functional reimplementation of the GoF patterns revisiting the Decorator and Chain of Responsibility patterns.

Gang of Four Patterns in a Functional Light: Part 2

About The Author
-

2 Comments

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>