GoF Patterns using Java 8 Lambdas - Part 1, the Factory Pattern

Published on Jiffle Development Blog
at http://localhost:4000/blog/2017/04/02/the-factory-pattern-using-java-8-lambda-evolved/

Posted on April 2, 2017

In Monika Goel’s DZone article The Factory Pattern Using Lambda Expressions in Java 8 she describes a nice way of using Java 8 Lambdas to implement the factory pattern.

However, passing ‘magic strings’ as the type selector is not the approach I usually take. More often I will define the types that can be created as an enum class which, while restricting the usage in certain ways (see ‘Conclusions and Discussion’, below), provides the benefit of compile-time checking of the selector, and removing the risk of runtime IllegalArgumentException failures.

One way enums can be used is as a container for compile-time properties. These properties can then be used to drive data-driven decisions - as opposed to having special-case progamming in the code. This pattern can be extended to embrace Lambdas, and it is this implementation approach that I consider in these examples.

These examples use Lombok annotations to reduce the boiler-plate text and make the code snippets clearer.

Classes Used from the DZone Example

The basic interface class:

public interface Shape {
   void draw();
}

The two implementing classes:

public class Rectangle implements Shape {
   public void draw() {
      System.out.println( "Inside Rectangle::draw() method.");
   }
}
public class Circle implements Shape {
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

Pattern I : Factory method using an enum, without parameterless constructors

For this version, we’ll implement the simplest factory pattern which is the static factory method, rather than a factory instance. We will revisit this later on. We’ll also stick with the version that uses constructors with no parameters.

Factory class: 1

public class ShapeFactory {
   @RequiredArgsConstructor
   @Getter( AccessLevel.PRIVATE)
   public static enum ShapeType {
      CIRCLE( Circle::new),
      RECTANGLE( Rectangle::new);

      private final Supplier<Shape> constructor;
   }

   public static Shape createShape( ShapeType type) {
      return type.getConstructor().get();
   }
}

This gives the following usage example:

public class FactoryPatternDemo {
   public static void main( String[] args) {
      //call draw method of circle
      ShapeFactory.createShape( ShapeType.CIRCLE).draw();
      //call draw method of Rectangle
      ShapeFactory.createShape( ShapeType.RECTANGLE).draw();      
   }
}

Pattern II : Factory method using an enum, with instance constructor parameters

Obviously most real-world factories take parameters with which to construct the instance. Let’s add parameters to control colour and line thickness to our first example:

Factory class: 2

public class ShapeFactory {
   @RequiredArgsConstructor
   @Getter( AccessLevel.PRIVATE)
   public static enum ShapeType {
      CIRCLE( (c, t) -> new Circle( c, t)),
      RECTANGLE( (c, t) -> new Rectangle( c, t));

      private final ShapeConstructor constructor;
   }

   @FunctionalInterface
   private interface ShapeConstructor {
      Shape create( Color colour, int thickness);
   }

   public static Shape createShape( ShapeType type, Color colour, int thickness) {
      return type.getConstructor().create( colour, thickness);
   }
}

Note that each concrete implementation will now need a constructor that matches the parameters used by the lambda for that class, otherwise it will generate a compiler error (albeit a rather unclear one that isn’t obvious to diagnose!). So, for example, the Circle implementation becomes:

@RequiredArgsConstructor
public class Circle implements Shape {
    private final Color colour;
    private final int thickness;

    public void draw() {
        System.out.println( MessageFormat.format( "Inside Circle::draw( {0}, {1}) method.", colour, thickness));
    }
}

Also, note that the lambdas in the enums could tailor the parameters to the specific constructor parameters needed by that specific class (as long as the parameters passed to the factory method are sufficient to build all the type classes).

The usage example might look now look like this:

public class FactoryPatternDemo {
   public static void main( String[] args) {
      //call draw method of circle
      ShapeFactory.createShape( ShapeType.CIRCLE, Color.RED, 2).draw();
      //call draw method of Rectangle
      ShapeFactory.createShape( ShapeType.RECTANGLE, Color.BLUE, 5).draw();      
   }
}

Pattern III : Returning a Factory Function

To me, this is where this gets really interesting. One of the main reasons to use the factory pattern is to decouple the points where the factory type is selected and where the instances are created. Ideally what we’d like to do is to construct the right type of factory in one part of the code, and then invoke the construction with parameters within a service that uses it.

We can do that here by changing the static factory method to return a lambda that constructs the instance. Conveniently, that is exactly the function signature that we already use in the enums.

This allows us to move the functional interface to the top-level class (since static methods are supported in Java 8). That static method also now becomes trivial, as it merely delegates to the getter in the relevant enum.

Thus the entire Factory implementation now looks like this: 3

@FunctionalInterface
public interface ShapeFactory {
   @RequiredArgsConstructor
   @Getter( AccessLevel.PRIVATE)
   public static enum ShapeType {
      CIRCLE( (c, t) -> new Circle( c, t)),
      RECTANGLE( (c, t) -> new Rectangle( c, t));

      private final ShapeFactory constructor;
   }

   Shape create( Color colour, int thickness);

   static ShapeFactory createFactory( ShapeType type) {
      return type.getConstructor();
   }
}

And it gives us the following usage examples:

@RequiredArgsConstructor
public class FactoryPatternDemo {
   private final ShapeFactory shapeFactory;

   public static void main( String[] args) {
      // first: static factory method example
      ShapeFactory.createFactory( ShapeType.CIRCLE).create( Color.RED, 2).draw();

      // second: inject factory into application instance and use later on
      ShapeFactory factory = ShapeFactory.createFactory( ShapeType.RECTANGLE);

      FactoryPatternDemo demoApp = new FactoryPatternDemo( factory);
      demoApp.drawShape( Color.BLUE, 3);
   }

   private void drawShape(Color colour, int thickness) {
      shapeFactory.create( colour, thickness).draw();
   }
}

Conclusions and Discussion

Personally, I think that the last pattern, being both flexible and compact, works really well and that it is one that I will use in future for most Factory Pattern Implementations. It is a succinct representation of the factory pattern while also being capable of being used as either a static factory method or an injectable factory instance.

Clearly the decision to make the enum an inner class of the interface may not to be everyone’s taste, but in this case the interface can be thought of as acting as a ‘namespace’ for the enum and the static factory method. Clearly in cases where the enum or factory implementations are more complicated then the enum can be made a top-level class.

Finally, there are scenarios, such as processing external (Web, Database) data where the type selector needs to be a string. The trivial factory method would look something like the following:

public static ShapeFactory createFactory( String type) {
   return ShapeType.valueOf( type.trim().toUpperCase()).getConstructor();
}

This will throw NullPointerException or IllegalArgumentException in cases where bad or null data is passed into the method. However whether or not these are useful responses depends on the context of both the data field (e.g. whether the REST data field is nullable or not) and how the created method will be used. In other words it is not a decision for the factory to make.

On the other hand, this pattern does have the advantage of allowing the factory method to return, as an error case, a function to construct the required error object, which is often helpful in allowing us to avoid plumbing the error all the way through the intermediate code. So for instance the factory method could return the following:

// create function that returns null
return (c, t) -> null;
// or
// create function that returns 'Null Object' that does nothing when the 'draw' method is called
return (c, t) -> new NullShape( c, t);