Nested Fluent Builders with Java 8

In the last post we looked at how to create an object using a fluent API in order to remove the code smell of a constructor with a long parameter list. We wrote the fluent methods directly onto the Invoice POJO, but we saw this broke the principle of Command Query Separation. In this post we will apply the Builder pattern, with the primary purpose of exploring how to make a nested fluent API using Java 8 lamdas. For more information on this pattern see Item 2 in the second edition of Effective Java by Josh Bloch.

We will start by removing the static factory methods from our Invoice class and replace them with a public static inner class, InvoiceBuilder. We will also need to add a static method (builder) that allows an instance of the InvoiceBuilder to be returned to any object that wants to create an Invoice. Finally, we will want to make sure that the constructor is private to force the use of the builder method to create an Invoice.

public class Invoice {
	...

	/**
	 * static factory method for builder
	 */
	public static InvoiceBuilder builder() {
		return new Invoice.InvoiceBuilder();
	}

	/**
	 * force use of builder()
	 */
	private Invoice() {
	}

	public static class InvoiceBuilder {
		// more to come
	}
}

As a static inner class we do not have access to non-static variables on the outer class. But if we manage an instance of the Invoice from inside our InvoiceBuilder we can take advantage of being an inner class and access the private instance variables through that instance.

Let’s add the creation of the Invoice object to our builder and manage it as an instance variable called managedInstance.

public class Invoice {
	...

	public static class InvoiceBuilder {
		private Invoice managedInstance = new Invoice();
	}
}

The next step is to move the fluent API from the outer class to our new inner class. Our original fluent API looked like this:

public Invoice suppliedBy(InvoiceActor actor) {
	this.invoiceSupplier = actor;
	return this;
}

Once we move it to the inner class we need to refactor each method so that the return type is InvoiceBuilder instead of Invoice. Then we will use the managedInstance (and our ability to access the private variables on the outer class) to store the value.

Our new fluent API on the builder looks like this now:

public InvoiceBuilder suppliedBy(InvoiceActor actor) {
	managedInstance.invoiceSupplier = actor;
	return this;
}

Finally, we need to add a build method so that the classes that want to create an Invoice can get an object reference to the instance we are building (ie the managedInstance).

public class Invoice {

	... 

	public static class InvoiceBuilder {
		private Invoice managedInstance = new Invoice();

		public InvoiceBuilder suppliedBy(InvoiceActor actor) {
			managedInstance.invoiceSupplier = actor;
			return this;
		}

		... rest of fluent API

		public Invoice build() {
			return managedInstance;
		}
	}
}

We can also refactor the classes InvoiceActor and InvoiceItem to use the builder pattern.

public class InvoiceActor {
	private String actorName;
	private InvoiceActorType actorType;

	public static InvoiceActorBuilder builder() {
		return new InvoiceActor.InvoiceActorBuilder();
	}

	private InvoiceActor() {
	}

	... accessor methods

	public static class InvoiceActorBuilder {
		private InvoiceActor managedInstance = new InvoiceActor();

		public InvoiceActorBuilder() {
		}

		public InvoiceActorBuilder named(String name) {
			managedInstance.actorName = name;
			return this;
		}

		public InvoiceActorBuilder as(InvoiceActorType type) {
			managedInstance.actorType = type;
			return this;
		}

		public InvoiceActor build() {
			return managedInstance;
		}
	}
}

Once we have completed these changes we can use the following code to create an Invoice with a fluent API that is based on the builder pattern:

Invoice inv = Invoice.builder()
		.createdOn(new Date())
		.withDocumentNumber("5150")
		.shippingOnTrailer("2112")
		.suppliedBy(InvoiceActor.builder()
			.named("101")
			.as(InvoiceActorType.DC)
			.build())
		.beingSentTo(InvoiceActor.builder()
			.named("42")
			.as(InvoiceActorType.STORE)
			.build())
		.with(InvoiceItem.builder()
			.as("TK421")
			.orderedQuantity(10)
			.shippedQuantity(8)
			.build())
		.with(InvoiceItem.builder()
			.as("FN2187")
			.orderedQuantity(5)
			.shippedQuantity(5)
			.build())
		.build();

This is more readable and maintainable than using setters or a constructor with a long parameter list. But because we have to deal with the call to builder and build for each of the classes used by Invoice it isn’t as clean as if we could nest our fluent builder API.

If we could nest our fluent API we could use the following code to create an Invoice.

Invoice inv = Invoice.builder()
	.createdOn(new Date())
	.withDocumentNumber("5150")
	.shippingOnTrailer("2112")
	.suppliedBy()
		.named("101")
		.as(InvoiceActorType.DC)
		.end()
	.beingSentTo()
		.named("42")
		.as(InvoiceActorType.STORE)
		.end()
	.withItem()
		.as("TK421")
		.orderedQuantity(10)
		.shippedQuantity(8)
		.end()
	.withItem()
		.as("FN2187")
		.orderedQuantity(5)
		.shippedQuantity(5)
		.end()
	.build();

Before Java 8, a nested fluent API was difficult to write as the following articles demonstrate (here, here, and here). With Java 8 and the ability to pass a functional interface we can take a crack at another approach to building a nested fluent API.

We will need to refactor our Invoice POJO again. We will use the suppliedBy method in the Builder as a case study just as we did above. Instead of returning an Invoice.Builder we will want to return the builder of the nested class – in this case an InvoiceActor.Builder. This will allow subsequent calls to populate values on the InvoiceActor. 

public InvoiceActor.Builder suppliedBy() {
	...
}

When we have finished building an InvoiceActor using the InvoiceActor.Builder we want to make sure that the object reference is stored in the Invoice attribute invoiceSupplier. To allow this to happen we take advantage of the new functional interfaces in Java 8. In the Invoice.Builder we will use the functional interface Consumer, which accepts one argument and returns nothing, just like a typical setter. Our version of this functional interface will accept an InvoiceActor as a parameter and will store the value on the invoiceSupplier attribute of the managedInstance (our Invoice object)We will pass this function, along with a reference to the Invoice.Builder to the InvoiceActor.Builder.

Our new version of the suppliedBy method looks like this.

public InvoiceActor.Builder suppliedBy() {
	Consumer<InvoiceActor> f = obj -> { managedInstance.invoiceSupplier = obj;};
	return new InvoiceActor.Builder(this, f);
}

That will necessitate some changes on the InvoiceActor.Builder. We will now need to accept a reference to the parent builder (Invoice.Builder) as well as the functional interface. These will be stored for future use.

public class InvoiceActor {

	...

	public static class Builder {
		private InvoiceActor managedInstance = new InvoiceActor();
		private Invoice.Builder parentBuilder;
		private Consumer<InvoiceActor> callback;

		public Builder() {
		}

		public Builder(Invoice.Builder b, Consumer<InvoiceActor> f) {
			this.parentBuilder = b;
			this.callback = f;
		}

		... fluent API

Modifying the Invoice.Builder method suppliedBy to return an InvoiceActor.Builder allows a transition from the Invoice.Builder fluent API to the InvoiceActor.Builder fluent API.

Invoice inv = Invoice.builder()
	.suppliedBy() // transition from Invoice.Builder to InvoiceActor.Builder
		.named("101")
		.as(InvoiceActorType.DC)
		.end() // transition from InvoiceActor.Builder to Invoice.Builder
	.build();

But once we are finished with the creation of the InvoiceActor object we need to transition back to the Invoice.Builder. That will allow us to finish populating its attributes and call the build method.

To accomplish this, we will signal the transition from the InvoiceActor.Builder back to the Invoice.Builder using a method named end.

The end method will invoke the Consumer function that we passed in allowing the object reference to the InvoiceActor to be stored in the parent Invoice.

public Invoice.Builder end() {
	callback.accept(managedInstance);
	return parentBuilder;
}

It will also return the instance of the builder that was passed in to the InvoiceActor.Builder allowing us to shift back to the Invoice.Builder.

Here is a what our InvoiceActor looks like once we have made all of these changes.

public class InvoiceActor {

	... attributes

	public static class Builder {
		private InvoiceActor managedInstance = new InvoiceActor();
		private Invoice.Builder parentBuilder;
		private Consumer<InvoiceActor> callback;

		public Builder() {
		}

		public Builder(Invoice.Builder b, Consumer<InvoiceActor> f) {
			this.parentBuilder = b;
			this.callback = f;
		}

		/**
		 * fluent setter for actor name
		 *
		 * @param name
		 * @return
		 */
		public Builder named(String name) {
			managedInstance.actorName = name;
			return this;
		}

		/**
		 * fluent setter for actor type
		 *
		 * @param type
		 * @return
		 */
		public Builder as(InvoiceActorType type) {
			managedInstance.actorType = type;
			return this;
		}

		/**
		 * build
		 * @return
		 */
		public InvoiceActor build() {
			return managedInstance;
		}

		/**
		 * end
		 * @return
		 */
		public Invoice.Builder end() {
			callback.accept(managedInstance);
			return parentBuilder;
		}

	}
}

By applying this concept to the rest of our Invoice, InvoiceActor, and InvoiceItem classes we can have a nested fluent API that uses the builder pattern.

All the code in this post is available from GitHub (link).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s