I Think every body should know their patterns. the GoF created that book a long time ago but still not every body knows them by heart?
One of the patterns that is becoming more prominent in the ever-changing world is the requirement for immutable objects grows. The definition found in the books:
- Defines an instance for creating an object but letting subclasses decide which class to instantiate
- Refers to the newly created object through a common interface
Well that’s not exactly what we right? Joshua Blog redefines the usage of the builder pattern slightly: He uses it to resolve the issue many constructors and constructor parameters. And uses setters to create an object. Sometimes i even think it more or less resembles a “fluent API”.
When implementing the builder pattern from Joshua Bloch one thing i always hear, do i really need to define and interface, create an inner class and do the private constructor stuff…
Actually you don’t need to. By the utilization of two annotations and a annotation processor the only thing you have to do is create an interface add the annotations, compile and you have all you need.
First have a look at the implementation as suggested by Joshua:
public class Widget {
public static class Builder {
public Builder(String name, double price) {
...
}
public Widget build()
{
...
}
public Builder manufacturer(String value) {
...
}
public Builder serialNumber(String value) {
...
}
public Builder model(String value) {
...
}
}
private Widget(Builder builder) {
...
}
}
There’s a Builder pattern that Joshua Bloch has briefly described in a couple of his “Effective Java Reloaded” sessions at Java One. This Builder is not necessarily a replacement for the original design pattern. The problems this Builder pattern can solve are too many constructors, too many constructor parameters, and over use of setters to create an object.
While the implementation is satisfactory it is quite a bit of typing and when you practice TDD you need to write test cases as well. Quite some time ago Jan Kees van Andel and myself thought this is too much work for the project we worked on at that time. We needed 70 plus objects like these. Is there an easy why to do this?
Well actually there is. The implementation of a builder is straight forward. create a class give it its getters and move all the setters to the builder and your done. Repeat that for all your immutable objects and you are done. No fancy logic what so ever. This calls for generation of code.
In order to make it simple in use we initially defined a annotation @Immutable. with this we could define an interface with all the getters and generate the implementation class.
The code we wrote looked like this:
@Immutable public interface Widget {
String getManufacturer();
String serialNumber();
String model();
}
The annotation process created the implementation. Almost correct, what about required values in the immutable object. For this we added an other annotation @Required. This allowed us to create both a builder and generate a class that required specific elements to be in there.
The book from Brian Goetz Java concurrency in practice uses the annotation @Immutable throughout the book. Source code for this is also available.
<dependency>
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
</dependency>
The implementation of the processor uses this annotation and the @Required annotation from our selves.
In order to use the annotation add the following to your maven build:
<dependencies>
<dependency>
<groupId>
nl.elucidator.patterns.builder.annotations
</groupId>
<artifactId>builder-annotations</artifactId>
<version>1.0.0</version>
</dependency>
<!--
This dependency is used in the compilation process
-->
<dependency>
<groupId>
nl.elucidator.patterns.builder.annotations
</groupId>
<artifactId>builder-annotations-processor</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
The source code for all this can be found at github: https://github.com/elucidator/builder-annotations.git
Working to get the artifacts in the Maven public repo.