So we implemented the builder pattern for most of our domain to help in understandability of what actually being passed to a constructor, and for the normal advantages that a builder gives. The one twist was that we exposed the builder through interfaces so we could chain required functions and unrequired functions to make sure that the correct parameters were passed. I was curious if there was an existing pattern like this.
Example below:
public class Foo
{
private int someThing;
private int someThing2;
private DateTime someThing3;
private Foo(Builder builder)
{
this.someThing = builder.someThing;
this.someThing2 = builder.someThing2;
this.someThing3 = builder.someThing3;
}
public static RequiredSomething getBuilder()
{
return new Builder();
}
public interface RequiredSomething { public RequiredDateTime withSomething (int value); }
public interface RequiredDateTime { public OptionalParamters withDateTime (DateTime value); }
public interface OptionalParamters {
public OptionalParamters withSeomthing2 (int value);
public Foo Build ();}
public static class Builder implements RequiredSomething, RequiredDateTime, OptionalParamters
{
private int someThing;
private int someThing2;
private DateTime someThing3;
public RequiredDateTime withSomething (int value) {someThing = value; return this;}
public OptionalParamters withDateTime (int value) {someThing = value; return this;}
public OptionalParamters withSeomthing2 (int value) {someThing = value; return this;}
public Foo build(){return new Foo(this);}
}
}
Example of how it’s called:
Foo foo = Foo.getBuilder().withSomething(1).withDateTime(DateTime.now()).build();
Foo foo2 = Foo.getBuilder().withSomething(1).withDateTime(DateTime.now()).withSomething2(3).build();
2
What you are doing is called a fluent interface.
From Fowler’s article:
In essence we create the various objects and wire them up together. If we can’t set up everything in the constructor, then we need to make temporary variables to help us complete the wiring – this is particularly the case where you’re adding items into collections.
Which is where @Frank was talking about “Phantom Types”. Also Fowler says
Probably the most important thing to notice about this style is that the intent is to do something along the lines of an internal DomainSpecificLanguage. Indeed this is why we chose the term ‘fluent’ to describe it, in many ways the two terms are synonyms. The API is primarily designed to be readable and to flow.
The way I have seen this discussed is to use “fluent” as an adjective, so in your case it would be a “fluent builder”.
What you are doing is essentially limiting the available methods via the type system. Java’s type system is unfortunately a bit on the weak side, so that you are rather limited in those respects.
When you look at stronger type systems in languages like Haskell, or Scala, you will come across the term type-level programming. In its full form, type-level programming can actually involve computations being made at compile-time. However, a less strict form of it is leveraging the type system in order to make implicit designs explicit, which is more ofter referred to as working with phantom types.
For example, you often have designs that require a user to first call method a
, before calling method b
of an object. In weaker type systems, you have to tell your users via comments, or design documents. But technically, they could still call b
first, so you might have to add a check of some sort, then throw an exception or something. Lots of work, and lots of error potentials – which you obviously recognized already.
Phantom types on the other hand are used to turn these implicit dependencies between method calls into an explicit compiler-checkable dependency by moving the different methods into different types.
Here is a nice example of phantom types in Java, and here’s a simple example comparing them in Haskell and Scala. Essentially, what these examples achieve is encoding a state into the type system. The second article, f.ex., constructs rocket objects, which can only be launch()
ed, if they have been filled with fuel and O2, i.e. you can call that method only if you have called other methods before it. In contrast to a solution, which simply implements all of these methods in one class, leveraging phantom types allows the compiler to automatically check this.
In turn, this also means, that in an IDE your auto-complete variations will be limited to the ones that make sense. If you have a rocket object which hasn’t been fueled yet, your auto-complete will simply not show you a launch()
method.
To come back to your example, you can replace the launch()
method with the build()
method and think about the different possible variants for which a build process is feasible. Then you define your phantom types based on the elements that need to be told to the builder, and find out if there is an order to them, or if there are multiple independent aspects. In the two posts I linked to, you can see an example with just one aspect (plane being in flight, or landed), and with two independent aspects (rocket being filled up with fuel and O2).
This approach is generally feasible for any number of aspects, but as you encode these into the type system, I would not recommended going too far with it. In terms of code quality/readability it relates to methods with a lot of parameters. We found these to be sub-optimal and causing more problems than what they’re worth. Similarly, phantom types being encoded in type parameters means that you end up with lots and lots of type parameters at the cost of readability. Notice that this only applies to independent aspects being encoded as phantom types. Once you have dependent ones, they are encoded via the same type parameter.
The pattern is officially called Step Builder, which makes usage of fluent interface.
As pointed out above, what you are doing is an example of a fluent interface. I use the pattern you are using quite a bit for the various wrappers I use in my integration tests. I call it “Fluent Expression Builder”. The advantages are that the resulting code is extremely readable and self-documenting. It also has significant advantages for composing new code using these API’s since the capabilities of the interface are self-discoverable as well as self-documenting. I.e. you don’t need to know if a parameter is required or not – the interface will ensure that you include it by making sure that at some point in the expression that specifying that parameter is the only available method.
Here is a great article that describes it by Per Lundholm:
http://blog.crisp.se/2013/10/09/perlundholm/another-builder-pattern-for-java
Is there a name for the Builder Pattern where the Builder is implemented via interfaces so certain parameters are required?
Short story long. No there’s not. Regardless the syntaxis sugar, the truth is that no interface forces consumers to call those methods entitled as “required”. Either to call them in a specific order. It would take to you to document well the builder and the interfaces for the consumer to be aware of the builder’s constraints.
We could validate the arguments right after calling the method .build()
, but still, the consumer might decide to ignore the methods. 1.
So we implemented the builder pattern for most of our domain to help
in understandability of what actually being passed to a constructor,
and for the normal advantages that a builder gives.
Builders won’t help you for that purpose. Moreover, they lack a bit of “clearness” since they are good at allowing consumers to instantiate objects with several and possible combinations (permutations) of attributes and with no order at all. They are also good at hiding required attributes since they (commonly) use default values to complete those attributes that were hidden intendedly.
At the moment of the building (when build()
is called), literally, consumers don’t know fully the state of the new object. Keep in mind that whoever created and consumed the builder might or might not be the same who triggers the .build()
. The consumption and the building might happen at different times and in different places.
If I were asked to achieve “clearness”, I would rather trust in Factories. For instance, in the factory method pattern or abstract factory pattern. However, it would depend on which pattern suits better my needs for creating objects.
You might find interesting this S.O question and its answers.
1: Of course, the build will fail over and over, till the consumer decide to call those methods. But then, the builder won’t make a big deal.
1