Refactoring: Introduce Builder

As I move more and more towards Immutable objects, I find myself extracting the building logic out from the usage of an object on a regular basis.  The basic format of this pattern is

mutable object =>  immutable object + builder

There are two aspects to using an object: creating it, and reading its state. This pattern splits those two aspects up into different object, providing a thread safe and scalable approach to sharing objects without restoring to locking. Modifications of the object are reflected by creating a new object, and then swapping out the immutable

There are two ways to go about making the builder. If the object is created as part of user interface or networking code, it make sense to use a generic map object, and in the build method, confirm that all required properties are there.
so code like:

if (inputStringKey.equals(‘x’)) obj.setX(inputStringValue);

becomes

builder.set(inlutStringKey, inputStringValue)
obj = builder.build();

If, on the other hand, the original object tends to be modified by other code, it makes sense to split the object so that the setters are on the builder object.  Thus the above code becomes:

builder.setX( x);
obj = builder.build();

The immutable object shouldn’t have getters, you should use public properties for them, make those properties public and immutable themselves. In Java, this means tagging them as final, in C++ make them const.

It the immutable object is fetched from a data store, there are two approaches to updating that datastore.  The simplest and least flexible is to have the builder update the store as part of the build function.  This mixes functionality, and means that you can only update a single object per transaction, but it does increase consistency of viewing the object across the system.  The more common approach is to have a deliberate call to store the immutable object in the datastore.  Often, this is a transactional store, with multiple immutable objects sent in bulk as an atomic commit.

Here are the steps to perform this refactoring:

1. Append the word builder to the class name

2. Create a new internal class for holding the immutable state of the the object and give it the name of the original object.  Give  the builder and instance member field of the internal type.  The internal class should have a no-args constructor.

3.  Push all fields down to the new class by  moving the fields .

4. Add a method called build that returns the internal type.

5.  Replace all calls of the form getProperty()  with

x=  builder.build() ;

x.property;

It is important to use the local variable, and to share it amongst all the places in the code that reference the local object.  If you don’t you will end up with one instance per reference, probably not what you want.

6.  Remove the getProperty methods on the builder.

7.  Give the internal class a constructor that takes all of its fields as parameters.  It should throw a standard exception if one of the parameters is null.

8.  Change the build method so that it returns a new instance, that gets all of its fields set via the new constructor.  This instance should just use the fields in the internal instance of the (soon-to-be) immutable inner object.  You will have to provide dummy initializers for the newly immutable fields.  The build method should thrown an exception if any of the values are missing or invalid.  It is usually required that you return all errors, not just the first one.  Thus this exception requires a collection of other errors.  These can be exceptions, or some other custom type, depending on the needs of your application.

9.   For each setProperty method on the builder, provide a copy of the field in the buidler object.  Make the corresponding field in the inner object immutable, and change the build method to use the field in the builder instead.

10. When you have finished step 9, you should have an immutable inner object. Provide an appropriate copy constructor.

11. Remove the no-args constructor from the immutable object.

12. remove the default values for the immutable fields.

You can now create a new builder object based on a map.  The keys of the map should be the names of the fields of the builder.  The Build method pulls the elements out of the map and uses them as the parameters for the constructor. This approach showcases one of the great limitations of Java introspection:  Parameter names are lost after compilation.  We only have access to the types and the order of the parameter names.  Thus, maintaining this map would be error prone.

A more performant approach is to extend the builder object above with a setProperty(String, String) method that sets the values of the builder fields directly.

Any Java object can act as a map if you use introspection.  Thus, you could do what the Bean API does and munge the key into the form “setX” by changeing the case on the first letter of the key name, and then calling

this.getClass().getMethod()

You could also use  property introspection like this:

this.getClass().getProperty(key)

Since you are using introspection, even though you are inside the class that should have access to private members, Java treats you as an outsider.  You can either drop permissions on the fields at compile time by making them public, or do so at runtime using one of various hacks.

This is one case where it makes sense to make the member fields public.  There is no information hiding going on here.  There may actually be some client codethat is better off calliing

builder.property = x

Than

builder.setProperty(x)

In C++, we have fewer choices, as there is no way either at run time nor at compile time to provide an iteration through the fields of a class.  The best you can do is to create a map of functors.  The keys of the map are again the fields of the builder, the values are functions which set the fields.  You end up with a setProperty function on the builder that looks like:

void setProperty(String& key, Strin& value){

propsetmap[key](value);

}

although with logic to handle erroneous keys.

A builder is a short lived, single threaded, mutable object.  It is up to the calling code to provide enough data to populate the builder.  The builder pattern works nicely with  inversion of control frameworks.  Code that uses an object should not call the builder directly, but rather fetch the object from the framework, where as code else where builds the object and adds it to the container.

If your code has interspersed sets and gets of properties, it is going to be really difficult to introduce the builder.  Chances are you are going to want to do additional refactorings to separate the concerns in your code.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.