Freitag, 14. Juni 2013

Thoughts on Builders

In my previous post I wrote about setters and that you should not use them in your domain model. The idea behind this is, that an object should be valid and consistent in every stage of its lifecycle. I.e. an empty address object is neither valid nor consistent. So I don't allow to create an empty address object. In fact an address may not be mutable. If I would change an attribute, it would be a completely new address and - the word new indicates this - should be a new object. So, if it is immutable, how to create it, so that it is valid and consistent from the beginning on? Btw., even objects that are mutable should be valid and consistent at any time. The easiest way to achieve this, is to provide a constructor that has a parameter for every non-optional attribute of the object.

public class Address {
  public Address(String street,
                 String streetNumber,
                 String zipCode,
                 String city,
                 String state,
                 String country) {
    ...
  }
}

Within that constructor you can ensure, that the object is valid and consistent from the beginning on. If the parameters are inconsistent, the constructor throws an exception. So far, so good, but as we can see from the example with an increasing number of attributes this is getting more and more ugly, since the number of constructor parameters increases (not to mention that your checkstyle plugin should complain). So what is the solution for this?

My favorite solution for this problem is the Builder pattern. Following the Single Responsibility Principle the builder is just responsible for creating the object. There are diffenent styles of builders, which I want to introduce now.

The JavaBean Style Builder

The first approach of a builder looks very close to a simple JSF bean (in fact it follows the JavaBeans standard). Just create a getter and setter for every attribute of the address.

Readers of my previous post may ask: in what kind is this builder better than the object with generated getters and setters I told you I hate. The answer is simple: The builder is just responsible to create a domain object. The actual validation of the parameters may take place at a single point in code and this point ensures that only valid and consistent address domain objects are created. We'll see later, where this single point in code may be (there are actually multiple choices). This is how you may use this kind of builder:

AddressBuilder builder = new AddressBuilder();
builder.setStreet(...);
builder.setStreetNumer(...);
builder.setZipCode(...);
builder.setCity(...);
builder.setState(...);
builder.setCountry(...);
Address address = builder.build();

This style of a builder is ok to me and it can even be used in JSF to create an object. But the code could be less verbose.

The Fluent-API Style Builder

We can reduce the code to create an address a lot, just by introducing method chaining for the builder. That means that every method of the builder returns this. This enables us to remove most of the references to the builder variable in the usage example above:

Address address = new AddressBuilder()
  .setStreet(...)
  .setStreetNumer(...)
  .setZipCode(...)
  .setCity(...)
  .setState(...)
  .setCountry(...)
  .build();

And we can even improve the readability of this code by using a fluent api:

Address address = new AddressBuilder()
  .forStreetNumber(...).ofStreet(...)
  .inCity(...).withZipCode(...)
  .lyingInState(...).ofCountry(...)
  .build();

Little problem with that code is that you cannot see from the builder code that it creates an address. You can further expose this by providing a static method within the builder:

public class AddressBuilder {
  public static AddressBuilder newAddress() {
    return new AddressBuilder();
  }
  ...
}

Using this method and a static import the above address creation code is even more readable:

Address address = newAddress()
  .forStreetNumber(...).ofStreet(...)
  .inCity(...).withZipCode(...)
  .lyingInState(...).ofCountry(...)
  .build();

Now we have seen two different styles of builders and how to use them. I don't think that there are two opinions about that the second variant is more elegant. Anyway, both variants have a method build() that returns an address object. For now we never mentioned how to implement this method. And this becomes tricky since we don't want the address to have a constructor with such number of arguments and we don't want to have an invalid and inconsistent address either. There are three ways to achieve this.

The obvious approach

The problem is, that we don't want to have many parameters in our constructor and we want to create our address at a single point in code, being valid and consistent? So let's give the Builder to the object and we are done.

public class Address {
  private String street;
  ...
  public Address(AddressBuilder builder) {
    street = validateStreet(builder.getStreet());
    ...
  }
  ...
}

The first disadvantage of this approach is, that our builder not only needs to have the fluent api, but also getters for all of its attributes. But that is not too bad. What people don't like with this approach is the dependency of the address to its builder. So what to do to invert this dependency? We can move the complete creation logic into a build() method of the builder. The problem is: How does the builder create the address object without using a constructor (and we neither want to use a constructor with many parameters nor a constructor taking the builder, because of the dependency)?

Package-access Builder

We can put the builder into the same package as the address and provide package-private (no modifier) setters for the attributes. This way you ensure that no code from outside the package can use the setters, but the builder can. Don't forget to make the constructor of the address package-private, too.

public class AddressBuilder {
  ...
  public Address build() {
    Address address = new Address();
    address.setStreet(validateStreet(street));
    ...
    return address;
  }
}

public class Address {
  ...
  Address() {
    // reduce visibility of the default constructor
  }

  public String getStreet() {
    return street;
  }

  // may just be used by the builder
  void setStreet(String initialStreet) {
    street = initialStreet;
  }
  ...
}

This approach works out very well and the creation logic for the address is at a single point in code, too: in the build() method. What I don't like at this approach is the re-introduction of setters, although they are package-private and may not be used from outside. It's just a feeling: If a programmer needs to modify an attribute, it's just to simple to put a public before that method and we are again at that point I was talking about in my previous post. This is why I don't like this approach much.

The Inner Class Builder

Remember, that our main problem is: How can the builder create an address and set its attributes without using a constructor that receives all this attributes and - in opposition to the previous approach - even without using setters. Well, this can be achieved, by making the builder an inner class of the address.

public class Address {
  public static Builder newAddress() {
    return new Address().new Builder();
  }
  private String street;
  ...
  public class Builder {
    public Builder ofStreet(String initialStreet) {
      street = validateStreet(initialStreet);
      return this;
    }
    public Address build() {
      validate();
      return Address.this;
    }
  }
}

This approach works out good as well, but it contains many code that a junior programmer may not have seen before, like new Address().new Builder() and Address.this. So this may be the most complex to understand code at a first glance, but if you once have understood it, it works out well, too. The drawback is, that the address object is created right before builder creation. But this is no problem, since code from outside can access the object only after it is validated by the builder.

It doesn't matter which approach you take, when it comes to JSF, you have the problem, that you cannot bind this builders directly to the UI (except for the JavaBean style one, which is not so elegant). In one of my next blog posts I will show what to do to directly use a fluent builder in JSF. Stay tuned.

The german version of this blog entry can be found here.

Keine Kommentare:

Kommentar veröffentlichen