The Agile Architect

Home » 2012 » December

Monthly Archives: December 2012

An Encapsulation-Preserving Builder Pattern in Java

The Builder pattern is used to construct complex objects. Typically, it is used to construct objects that would be impractical to construct with a single constructor call due to the large amount of parameters of many different ways than an object can be constructed.

Here is a usage example of a builder from the Quartz Job Scheduling Library.

JobDetail job = JobBuilder.newJob().
                withDescription("A quick and dirty job").
                withIdentity("RushJob").
                usingJobData("checkTwice", false).
                build();

A good builder must satisfy the following requirements:

  1. It must not allow the creation of invalid, or partially created, objects.
  2. It must hide the implementation details of the object being built.
  3. It must hide the implementation details of the builder itself.

The Quartz builder presented above is a pretty good builder. It guarantees the constructed object is complete, it has simple intuitive build steps, each returning a Builder object in order to allow for convenient method chaining. Where it falls a bit short is in satisfying requirement #2. The reason is that, as a user of the library I can cheat and explicitly create a JobDetailImpl object (the implementation of JobDetail) and call setter methods to create a possibly invalid Job. In other words, the Quartz Builder does not preserve the encapsulation of the JobDetail object.

The root of the problem is that in order to satisfy #2, the constructor and setter of the object must be private, which consequently means that no external object can use them.  This, in turn, implies that if the Builder were an external object it could have no access to the constructors and setters. If you are really serious about encapsulation, you need to work around this problem, and this is what I am demonstrating in this article.

Recipe for a Builder the preserves encapsulation

Here is a simple recipe for creating a fully encapsulated builder:

  1. Define an interface for the object you are creating. The interface should expose only methods necessary to use the object, not build it.
  2. Define an interface for the object’s builder.
  3. In the implementation of the target object, make the object’s constructor and setters private.
  4. Define the builder implementation as an inner private class of the target object.
  5. Provide a static method  that gives you an instance of the builder.

To put this recipe in action, let’s see how it can be used to create a Finite State Machine (FSM). An FSM is a good example of an object that requires a builder since there is an infinite number of ways to create them.

1. Define the interface of the Finite State Machine:

public interface FiniteStateMachine<S>  {

    S getInitialState();

    S getNextState(S from, char symbol);

    boolean accepts(String word);
}

2. Define the interface for the builder:

public interface FSMBuilder<S> {

    FSMBuilder<S> setInitialState(S initialState) 
                   throws BuilderException;

    FSMBuilder<S> addFinalState(S state) throws BuilderException;

    FSMBuilder<S> addTransition(S from, S to, char c)
            throws BuilderException;

    FiniteStateMachin<S> build() throws BuilderException;

}

This interface is characteristic of builder interfaces. The first three methods provide building blocks, whereas the last method, which must always be present, returns the final constructed object – or throws an exception if the object is not complete.

The following snippet of code illustrates steps 3, 4, and 5.

3. FSM is the implementation of FiniteStateMachine. Note that its constructor and internal variables are private.

4. PrivateFSMBuilder, which implements FSMBuilder, is an inner private class, only providing access to valid build steps. This is the key to making the builder have access to the inner workings of the class without exposing them to the outside world.

5. Note that in the last line of code we provide the only possible way to construct a new FSM, by getting a hold of a builder interface.

public class FSM<S> implements FiniteStateMachine<S> {

    private StateTransitionTable<S> transitionTable = new StateTransitionTable<S, C>();

    private FSM() {}

    private class PrivateFSMBuilder implements FSMBuilder<S> {

        @Override
        public FSMBuilder<S> setInitialState(S initialState)
                throws FABuilderException {
            if (null != FSM.this.initialState) {
                throw new BuilderException("Initial state already set.");
            }
            FSM.this.initialState = initialState;
            states.add(initialState);
            return this;
        }

        @Override
        public FSMBuilder<S> addFinalState(S state)
                throws BuilderException {
            FSM.this.addFinalState(state);
            return this;
        }

        @Override
        public FSMBuilder<S addTransition(S from, S to, char c)
                throws BuilderException {
            FSM.this.addTransition(from, to, c);
            return this;
        }

        @Override
        public FiniteStateMachine<S> build()
                throws FABuilderException {
            if (FSM.this.getInitialState() == null) {
                throw new BuilderException("Initial state is not specficied.");
            }
            if (FSM.this.finalStates.isEmpty()) {
                throw new BuilderException(
                        "There must be at least one final state");
            }
            return FSM.this;
        }
    }

    //More FSM code goes here...

    private FSMBuilder<S> buidler = new PrivateFSMBuilder();

    public static <S> FSMBuilder<S> newFSM() {
        return new FSM<S>().buidler;
    }

Here is an example of how to use the FSM builder to build an FSM:

FSMBuilder<String> builder = FSM.newFSM();
FiniteStateMachine<String> machine =      
    builder.setInitialState("A")
           .addFinalState("B")
           .addTransition("A", "A", '0')
           .addTransition("A", "B", '1')
           .addTransition("B", "A", '1')
           .addTransition("B", "B", '0')
           .build();

Conclusion

In this article we visited a well known Design Pattern, termed the Builder. We demonstrated how to implement a Builder pattern in Java in a manner that preserves the Encapsulation principle both for the object being built and the builder itself.

You can see this builder pattern in action for an actual Finite State Machine by downloading the source code for the JavaFSM project in Sourceforge http://sourceforge.net/projects/javafsm/