No, this is not the Builder pattern. It's valid Java, and it will compile and run. But your buildNewDrink() method, whether it's called build() or buildNewDrink() or something else, is just a simple Factory Method that creates a CoffeeDrink. Those other methods are like setter methods that happen to return themselves.
The static nested Builder class is necessary. While holding off on creating the class instance, it can perform validation logic to ensure that an invalid object is not created. I'm not sure that there is an invalid state to a CoffeeDrink as you have it, but if it did, with your code, it would be possible to create a CoffeeDrink and have it in an invalid state after it was created, but before other methods were called. The Builder pattern eliminates this possibility by validating the data before building the instance. It also eliminates the need for constructor explosion, where lots of constructors with all possible combinations of parameters are needed, to cover all possible cases.
Videos
No, this is not the Builder pattern. It's valid Java, and it will compile and run. But your buildNewDrink() method, whether it's called build() or buildNewDrink() or something else, is just a simple Factory Method that creates a CoffeeDrink. Those other methods are like setter methods that happen to return themselves.
The static nested Builder class is necessary. While holding off on creating the class instance, it can perform validation logic to ensure that an invalid object is not created. I'm not sure that there is an invalid state to a CoffeeDrink as you have it, but if it did, with your code, it would be possible to create a CoffeeDrink and have it in an invalid state after it was created, but before other methods were called. The Builder pattern eliminates this possibility by validating the data before building the instance. It also eliminates the need for constructor explosion, where lots of constructors with all possible combinations of parameters are needed, to cover all possible cases.
According to the GoF reference, build() isn't required. The original reference doesn't use chaining, and there is a getResult() step at the end of the Director.construct() method. The Director class takes care of encapsulating the build process, so Clients don't need to worry if they're building things correctly. It's the responsibility of the Director.
Here's the sequence diagram from the GoF reference on Builder:
It's so you can be immutable AND simulate named parameters at the same time.
Person p = personBuilder
.name("Arthur Dent")
.age(42)
.build()
;
That keeps your mitts off the person until it's state is set and, once set, won't let you change it. Yet every field is clearly labeled. You can't do this with just one class in Java.
It looks like you're talking about Josh Blochs Builder Pattern. This should not be confused with the Gang of Four Builder Pattern. These are different beasts. They both solve construction problems, but in fairly different ways.
Of course you can construct your object without using another class. But then you have to choose. You lose either the ability to simulate named parameters in languages that don't have them (like Java) or you lose the ability to remain immutable throughout the objects lifetime.
Immutable example, has no names for parameters
Person p = new Person("Arthur Dent", 42);
Here you're building everything with a single simple constructor. This will let you stay immutable but you loose the simulation of named parameters. That gets hard to read with many parameters. Computers don't care but it's hard on the humans.
Simulated named parameter example with traditional setters. Not immutable.
Person p = new Person();
p.name("Arthur Dent");
p.age(42);
Here you're building everything with setters and are simulating named parameters but you're no longer immutable. Each use of a setter changes object state.
So what you get by adding the class is you can do both.
Validation can be performed in the build() if a runtime error for a missing age field is enough for you. You can upgrade that and enforce that age() is called with a compiler error. Just not with the Josh Bloch builder pattern.
For that you need an internal Domain Specific Language (iDSL).
This lets you demand that they call age() and name() before calling build(). But you can't do it just by returning this each time. Each thing that returns returns a different thing that forces you to call the next thing.
Use might look like this:
Person p = personBuilder
.name("Arthur Dent")
.age(42)
.build()
;
But this:
Person p = personBuilder
.age(42)
.build()
;
causes a compiler error because age() is only valid to call on the type returned by name().
These iDSLs are extremely powerful (JOOQ or Java8 Streams for example) and are very nice to use, especially if you use an IDE with code completion, but they are a fair bit of work to set up. I'd recommend saving them for things that will have a fair bit of source code written against them.
Why use/provide a builder class:
- To make immutable objects — the benefit you've identified already. Useful if the construction takes multiple steps. FWIW, immutability should be seen a significant tool in our quest to write maintainable and bug free programs.
- If the runtime representation of the final (possibly immutable) object is optimized for reading and/or space usage, but not for update. String and StringBuilder are good examples here. Repeatedly concatenating strings is not very efficient, so the StringBuilder uses a different internal representation that is good for appending — but not as good on space usage, and not as good for reading and using as the regular String class.
- To clearly separate constructed objects from objects under construction. This approach requires a clear transition from under-construction to constructed. For the consumer, there is no way to confuse an under-construction object with a constructed object: the type system will enforce this. That means sometimes we can use this approach to "fall into the pit of success", as it were, and, when making abstraction for others (or ourselves) to use (like an API or a layer), this can be a very good thing.
The GenericBuilder
The idea for building mutable objects (immutable objects are addressed later on) is to use method references to setters of the instance that should be built. This leads us to a generic builder that is capable of building every POJO with a default constructor - one builder to rule them all ;-)
The implementation is this:
public class GenericBuilder<T> {
private final Supplier<T> instantiator;
private List<Consumer<T>> instanceModifiers = new ArrayList<>();
public GenericBuilder(Supplier<T> instantiator) {
this.instantiator = instantiator;
}
public static <T> GenericBuilder<T> of(Supplier<T> instantiator) {
return new GenericBuilder<T>(instantiator);
}
public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value) {
Consumer<T> c = instance -> consumer.accept(instance, value);
instanceModifiers.add(c);
return this;
}
public T build() {
T value = instantiator.get();
instanceModifiers.forEach(modifier -> modifier.accept(value));
instanceModifiers.clear();
return value;
}
}
The builder is constructed with a supplier that creates new instances and then those instances are modified by the modifications specified with the with method.
The GenericBuilder would be used for Person like this:
Person value = GenericBuilder.of(Person::new)
.with(Person::setName, "Otto").with(Person::setAge, 5).build();
Properties and further Usages
But there is more about that builder to discover.
For example, the above implementation clears the modifiers. This could be moved into its own method. Therefore, the builder would keep its state between modifications and it would be easy create multiple equal instances. Or, depending on the nature of an instanceModifier, a list of varying objects. For example, an instanceModifier could read its value from an increasing counter.
Continuing with this thought, we could implement a fork method that would return a new clone of the GenericBuilder instance that it is called on. This is easily possible because the state of the builder is just the instantiator and the list of instanceModifiers. From there on, both builders could be altered with some other instanceModifiers. They would share the same basis and have some additional state set on built instances.
The last point I consider especially helpful when needing heavy entities for unit or even integration tests in enterprise applications. There would be no god-object for entities, but for builders instead.
The GenericBuilder can also replace the need for different test value factories. In my current project, there are many factories used for creating test instances. The code is tightly coupled to different test scenarios and it is difficult to extract portions of a test factory for reuse in another test factory in a slightly different scenario. With the GenericBuilder, reusing this becomes much easier as there is only a specific list of instanceModifiers.
To verify that created instances are valid, the GenericBuilder could be initialized with a set of predicates, which are verified in the build method after all instanceModifiers are run.
public T build() {
T value = instantiator.get();
instanceModifiers.forEach(modifier -> modifier.accept(value));
verifyPredicates(value);
instanceModifiers.clear();
return value;
}
private void verifyPredicates(T value) {
List<Predicate<T>> violated = predicates.stream()
.filter(e -> !e.test(value)).collect(Collectors.toList());
if (!violated.isEmpty()) {
throw new IllegalStateException(value.toString()
+ " violates predicates " + violated);
}
}
Immutable object creation
To use the above scheme for the creation of immutable objects, extract the state of the immutable object into a mutable object and use the instantiator and builder to operate on the mutable state object. Then, add a function that will create a new immutable instance for the mutable state. However, this requires that the immutable object either has its state encapsulated like this or it be changed in that fashion (basically applying parameter object pattern to its constructor).
This is in some way different than a builder was used in pre-java-8 times. There, the builder itself was the mutable object that created a new instance at the end. Now, we have a separation of the state a builder keeps in a mutable object and the builder functionality itself.
In essence
Stop writing boilerplate builder patterns and get productive using the GenericBuilder.
public class PersonBuilder {
public String salutation;
public String firstName;
public String middleName;
public String lastName;
public String suffix;
public Address address;
public boolean isFemale;
public boolean isEmployed;
public boolean isHomewOwner;
public PersonBuilder with(
Consumer<PersonBuilder> builderFunction) {
builderFunction.accept(this);
return this;
}
public Person createPerson() {
return new Person(salutation, firstName, middleName,
lastName, suffix, address, isFemale,
isEmployed, isHomewOwner);
}
}
Usage
Person person = new PersonBuilder()
.with($ -> {
$.salutation = "Mr.";
$.firstName = "John";
$.lastName = "Doe";
$.isFemale = false;
})
.with($ -> $.isHomewOwner = true)
.with($ -> {
$.address =
new PersonBuilder.AddressBuilder()
.with($_address -> {
$_address.city = "Pune";
$_address.state = "MH";
$_address.pin = "411001";
}).createAddress();
})
.createPerson();
Refer: https://medium.com/beingprofessional/think-functional-advanced-builder-pattern-using-lambda-284714b85ed5
Disclaimer: I am the author of the post
Below are some reasons arguing for the use of the pattern and example code in Java, but it is an implementation of the Builder Pattern covered by the Gang of Four in Design Patterns. The reasons you would use it in Java are also applicable to other programming languages as well.
As Joshua Bloch states in Effective Java, 2nd Edition:
The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters.
We've all at some point encountered a class with a list of constructors where each addition adds a new option parameter:
Pizza(int size) { ... }
Pizza(int size, boolean cheese) { ... }
Pizza(int size, boolean cheese, boolean pepperoni) { ... }
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }
This is called the Telescoping Constructor Pattern. The problem with this pattern is that once constructors are 4 or 5 parameters long it becomes difficult to remember the required order of the parameters as well as what particular constructor you might want in a given situation.
One alternative you have to the Telescoping Constructor Pattern is the JavaBean Pattern where you call a constructor with the mandatory parameters and then call any optional setters after:
Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);
The problem here is that because the object is created over several calls it may be in an inconsistent state partway through its construction. This also requires a lot of extra effort to ensure thread safety.
The better alternative is to use the Builder Pattern.
public class Pizza {
private int size;
private boolean cheese;
private boolean pepperoni;
private boolean bacon;
public static class Builder {
//required
private final int size;
//optional
private boolean cheese = false;
private boolean pepperoni = false;
private boolean bacon = false;
public Builder(int size) {
this.size = size;
}
public Builder cheese(boolean value) {
cheese = value;
return this;
}
public Builder pepperoni(boolean value) {
pepperoni = value;
return this;
}
public Builder bacon(boolean value) {
bacon = value;
return this;
}
public Pizza build() {
return new Pizza(this);
}
}
private Pizza(Builder builder) {
size = builder.size;
cheese = builder.cheese;
pepperoni = builder.pepperoni;
bacon = builder.bacon;
}
}
Note that Pizza is immutable and that parameter values are all in a single location. Because the Builder's setter methods return the Builder object they are able to be chained.
Pizza pizza = new Pizza.Builder(12)
.cheese(true)
.pepperoni(true)
.bacon(true)
.build();
This results in code that is easy to write and very easy to read and understand. In this example, the build method could be modified to check parameters after they have been copied from the builder to the Pizza object and throw an IllegalStateException if an invalid parameter value has been supplied. This pattern is flexible and it is easy to add more parameters to it in the future. It is really only useful if you are going to have more than 4 or 5 parameters for a constructor. That said, it might be worthwhile in the first place if you suspect you may be adding more parameters in the future.
I have borrowed heavily on this topic from the book Effective Java, 2nd Edition by Joshua Bloch. To learn more about this pattern and other effective Java practices I highly recommend it.
Consider a restaurant. The creation of "today's meal" is a factory pattern, because you tell the kitchen "get me today's meal" and the kitchen (factory) decides what object to generate, based on hidden criteria.
The builder appears if you order a custom pizza. In this case, the waiter tells the chef (builder) "I need a pizza; add cheese, onions and bacon to it!" Thus, the builder exposes the attributes the generated object should have, but hides how to set them.