Smarter ValueObjects & an (even more) elegant Builder

Value Objects (VOs) are prevalent and needed in traditional Java programming. They’re almost everywhere —  to hold information within a process, for message-passing, and various other areas.

Apart from having getters and setters for the properties, on several occasions, there’s a requirement for these VOs to implement equals() and hashCode(). Developers usually hand-write these methods or use the modern IDE templates to generate them. This works fine initially or until there’s a need to update the VOs with one or more additional properties.

With an update, the baggage that comes with new properties includes:

  • a new set of getters and setters,
  • updates required to equals(), hashCode(), and,
  • update required to toString(),if needed

This is, of course, cumbersome, error-prone, and the simple VO soon starts looking like an airplane cockpit!

Google’s AutoValue framework is a smart approach to address this issue. With just a couple of annotations, almost all of the “junk” is done away with, and the class becomes smarter — any future property updates, including getters, setters, as well as equals()*, hashCode()** and toString() are all handled automagically!

The VO then just looks like a basic set of properties of the given type, like so:

import com.google.auto.value.AutoValue;

@AutoValue
abstract class CartItem {
    abstract int itemCode();

    abstract int quantity();

    abstract int price();

    static CartItem create(int itemCode, int quantity, int price) {
        return new AutoValue_CartItem(itemCode, quantity, price);
    }
}

Note the default presence of a static factory method create(), as suggested in Effective Java [Bloch, 2017], Item 2.

The use of this annotated VO would be no different from a typical one. For instance, the CartItem defined above would have a simple invocation like this:

@Test
public void create() throws Exception {
    CartItem item1 = CartItem.create(10,33, 12);
    CartItem item2 = CartItem.create(10,33, 12);

    assertEquals(item1, item2); // this would be true
}

Apart from the default support for a static factory, AutoValue also supports Builder classes, within the VOs. Armed with this knowledge, let’s take another jab at the example in my previous post on Builders.
We continue with the same Cake example and add the required annotations and modifiers. The updated version of the class would then be:

import com.google.auto.value.AutoValue;

@AutoValue
abstract class Cake {
    // Required params
    abstract int flour();
    abstract int bakingPowder();

    // Optional params
    abstract int eggs();
    abstract int sugar();
    abstract int oil();

    static Maker builder(int flourCups, int bkngPwdr) {
        // return builder instance with defaults for non-required field
        return new AutoValue_Cake.Builder().flour(flourCups).bakingPowder(bkngPwdr).eggs(0).sugar(0).oil(0);
    }

    @AutoValue.Builder
    abstract static class Maker {
        abstract Maker flour(int flourCups);
        abstract Maker bakingPowder(int bkngPwdr);
        abstract Maker eggs(int eggCount);
        abstract Maker sugar(int sugarMg);     
        abstract Maker oil(int oilOz);

        abstract Cake build();
    }
}

Observe that:

  • the member Builder class (named Maker here) just needs to be marked with @AutoValue.Builder annotation, and the framework takes care of everything else
  • in the parent class, we could also have had a no-arg builder() method but we specifically want to have only one way of building this class — with the required params
  • as shown above, the optional parameters should be set to their default values since we want the flexibility of choosing only the relevant optional params. [With non-primitive members, @Nullable can be used.]

Just to complete the discussion, here is an example of the ease with which this new builder can be invoked:

@Test
public void makeCakes() {

    // Build a cake without oil
    Cake cakeNoOil = Cake.builder(2, 3).sugar(2).eggs(2).build();

    assertNotNull(cakeNoOil);

    // Check that it has 0 oil
    assertEquals(0, cakeNoOil.oil()); // default

    // Make cake with oil
    Cake cakeWOil = Cake.builder(2, 3).sugar(2).oil(1).eggs(2).build();

    // Obviously, both the cakes are different
    assertNotEquals(cakeNoOil, cakeWOil); // valid

    // Another cake that's same as cake w/ oil
    Cake anotherCakeWOil = Cake.builder(2, 3).sugar(2).oil(1)
            .eggs(2).build();

    assertEquals(cakeWOil, anotherCakeWOil); // valid
}

There are many other fine-grained things that can be done while using AutoValue, like specifying getters for specific properties or customizing toString(), etc.

It’s impressive how AutoValue facilitates writing and static factory methods and builders quickly — taking the headache out of defining and updating VOs.

[Full implementation of the abovementioned example is here.]

Further reading:

  1. AutoValue with Builders
  2. Project Lombok also addresses the VO update issue, along with other things

* Effective Java [Bloch, 2017], Item 10
** Effective Java [Bloch, 2017], Item 11

The elegance of Builder pattern

Paraphrasing Josh Bloch in Effective Java [Bloch, 2017, Item 2]:

While creating objects, in cases where the number of optional parameters of an object is considerable, say 4 or more, one might think of static factory methods [Bloch, 2017, Item 1] as a solution — but they’re more suitable for a small set of parameters. When there are several optional params, static factories cannot be used as it’s cumbersome to imagine and cater to all possible parameter combinations. Another approach that’s proposed in such cases is using JavaBeans but it has its own shortcomings.

Therefore, we usually go with multiple (telescoping) constructors for such requirements. For example:

public Cake(int oilTbsp, int flourMg){
  this(oilTbsp, flourMg, 0);
}

public Cake(int oilTbsp, int flourMg, int eggCount){
  this(oilTbsp, flourMg, eggCount, 0);
}

public Cake(int oilTbsp, int flourMg, int eggCount, int bakingPowderMg){
  this(oilTbsp, flourMg, eggCount, bakingPowederMg);
}

//...

Such implementations, although purpose-serving, are a bit contrived in that the class client needs to tally the parameters accurately. Consider a large parameter list, and this would be an overkill.

A variation of Builder pattern [Gamma, 1995], is what Bloch suggests, for such cases. In it, a builder class is a static member of the class it builds, for example:

public class Cake{
  //...
  private Cake(Builder builder){
    //...
  }

  public static class Builder{
    //...
  }
}

Since the original constructor is hidden, the client first gets a reference to the Builder class — passing all the required params to its constructor or static factory. The client then calls setters on the returned builder object to set the optional parameters of interest. Finally, the client makes a call to the build() method to generate an immutable object.
Since the builder setter methods return the builder itself, the invocations can be chained, like so:

// Set only the parameters of interest
Cake cake = new Cake.Builder(350, 45).egg(2).sugar(240).cocoa(35)...build();

As is apparent, this is intuitive as well as concise.

A builder can be further enhanced by enabling it to build more than one object, based on parameters. One has to be cautious, however, to disallow building an object of an inconsistent state. This can be ensured by validating the passed parameters as early as possible and throwing a suitable exception.

Builders can also be used to automate certain tasks and fill in the fields. For example, autoincrementing the object Id, etc.

As Josh Bloch advises, we should be using Builders as often as possible, especially in cases where the number of parameters is significant. They’re a simple and elegant alternative to telescoping constructors or JavaBeans.

[Full implementation of the Cake builder example is here.]