Monika Goel’s DZone article The Factory Pattern Using Lambda Expressions in Java 8 describes a nice way of using Java 8 Lambdas to implement the factory pattern. In this post I look at extending that approach using enum types to wrap the function definitions and evolving the pattern to neaten the implementation.
To recap on Monika Goel’s article, she describes implementing the Factory Pattern as a map of lambdas implementing the Supplier
interface keyed by the String selectors used to determine which type to construct.
Clearly ‘magic strings’ selectors give a very flexible solution, however more often I will define the type selectors using 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 programming in the code. This pattern can be extended to embrace Lambdas, and it is this implementation approach that I consider in these examples.
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. Yes, I know this restricts testability - we will revisit this later on! For this pattern we’ll also stick with the version that uses parameterless constructors.
Factory class: 1
public class ShapeFactory {
public static enum ShapeType {
CIRCLE( Circle::new ),
RECTANGLE( Rectangle::new );
private final Supplier<Shape> constructor;
ShapeType( Supplier<Shape> constructor ) {
this.constructor = constructor;
}
private Supplier<Shape> getConstructor() {
return this.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( ShapeFactory.ShapeType.CIRCLE ).draw();
//call draw method of Rectangle
ShapeFactory.createShape( ShapeFactory.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 {
public static Shape createShape( ShapeType type, Color colour, int thickness ) {
return type.getConstructor().create( colour, thickness );
}
public static enum ShapeType {
// use either explicit or implicit constructor references
CIRCLE( Circle::new ),
RECTANGLE( ( c, t ) -> new Rectangle( c, t ) );
private final ShapeConstructor constructor;
ShapeType( ShapeConstructor constructor ) {
this.constructor = constructor;
}
private ShapeConstructor getConstructor() {
return this.constructor;
}
}
@FunctionalInterface
private interface ShapeConstructor {
Shape create( Color colour, int thickness );
}
}
Note the two different variants of lambda implementation used for Circle
and Rectangle
. Both classes have two parameter constructor, but for Circle
we use the method reference of the constructor in the same way as Pattern I. Which is better, I think, is down to the reader to judge: both give the same error if there is no constructor with the required number of parameters.
Note that the Circle implementation now looks like this:
public class Circle implements Shape {
private final Color colour;
private final int thickness;
public Circle( Color colour, int thickness ) {
this.colour = colour;
this.thickness = 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( CIRCLE, Color.RED, 2 ).draw();
//call draw method of Rectangle
ShapeFactory.createShape( 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 {
static ShapeFactory createFactory( ShapeType type ) {
return type.getConstructor();
}
Shape create( Color colour, int thickness );
enum ShapeType {
// use either explicit or implicit constructor references
CIRCLE( Circle::new ),
RECTANGLE( ( c, t ) -> new Rectangle( c, t ) );
private final ShapeFactory constructor;
ShapeType( ShapeFactory constructor ) {
this.constructor = constructor;
}
private ShapeFactory getConstructor() {
return this.constructor;
}
}
}
And it gives us the following usage examples:
public class FactoryPatternDemo {
private final ShapeFactory shapeFactory;
public FactoryPatternDemo( ShapeFactory shapeFactory ) {
this.shapeFactory = shapeFactory;
}
public static void main( String[] args ) {
// first: static factory method example
ShapeFactory.createFactory( CIRCLE ).create( Color.RED, 2 ).draw();
// second: inject factory into application and use later on
ShapeFactory factory = ShapeFactory.createFactory( 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.
Using Enum as an Inner Class
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.
Using Text Selectors
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 );
Support for Testing
The ability to support testing and mocking behaviour is very important, as factories are a key pattern in allowing test objects to be injected instead of real dependencies.
Patterns I & II are problematic from that point of view. Pattern III, however, supports testing really well. It is very easy to inject a lambda or closure which returns a mock, stub or spy to facilitate testing checks.
-
See Full Factory I example in Github ↩