Search code examples
javadesign-patternsgenericsbuilderfluent-interface

How to implement builder pattern with fluent-interface with immutable results and extended interfaces?


I would like to implement a builder with a fluent interface.

Requirements

To make things more difficult there are two additional requirements:

  1. I would like the returned objects to be immutable so that it can be used as follows and the interface should be extendible in derived interfaces:

    ConcreteBuilder b1 = builder().setValue(1);
    ConcreteBuilder b2 = b1.setValue(2);
    
    ComplexObject o1 = b1.build();
    o1.getValue(); // should return 1
    
    ComplexObject o2 = b2.build();
    o2.getValue(); // should return 2
    

    First an issue open for discussion: should the builder be immutable so that it has above semantics? (it's briefly commented on by Jonathan in How to create an immutable builder of an immutable class that contains a set?)

  2. The builder interface should be extendible:

    interface Builder<T extends Builder<T>> {
         T setValue(int v);
    }
    
    interfacte BuilderEx<T extends BuilderEx<T>> {
         T someOtherOpp();
    }
    

    (it uses self referencing generics as described by Eamonn McManus1)

Implementation

To satisfy the first requirement AbstractBuilder's implementation is similar to the one in How to create an immutable builder of an immutable class that contains a set? :

class AbstractBuilder<T extends Builder<T>> implements Builder<T> {
    T setValue(final int v) {
        return castToConcrete(new AbstractBuilder<T>() {
            // with build() overridden to use v
        }
    }
}

castToConcrete should be similar to self()1 and getThis()2: converting a Builder<T> to ConcreteBuilder or ConcreteBuilderEx depending on T . The problem is how to implement castToConcrete

Since the builder methods create new instances the method of overriding getThis in ConcreteBuilder doesn't work. Using a "functor"3 supplied to the constructor of AbstractBuilder and stored in a field enables conversion of Builder<T> to T. Add the following field (it uses the Guava library3 to AbstractBuilder:

Function<Builder<T>, T> castToConcrete;

Problem

My current implementation of castToConcrete turns quite ugly:

  1. it's an if-else tree on instanceof
  2. when input instanceof Builder and not input instanceof BuilderEx a wrapper class which needs to define all methods in BuilderEx, forward the ones in Builder to input and perform a default or nil operation for the others.
  3. when input instanceof BuilderEx it casts input to ConcreteBuilderEx even though input is probably not an actual instance of ConcreteBuilderEx but an instance of an annonymous subclass of AbstractBuilderEx (hoping that this works.)[edit by OP]Doesn't work: we need a wrapper for every interface[/edit]

What is a better/cleaner way to do this?


Solution

  • The clean way is to not use an AbstractBuilder but create a specific builder for each type you need to build. You get a fluent interface by making builder methods readable and customized to the object you are trying to build. If you need to create lots of builders you can use code generation, but the methods won't be as well designed.