What about Builders?

24 views
Skip to first unread message

v6ak

unread,
Aug 3, 2009, 12:45:16 PM8/3/09
to Project Lombok
Immutable classes are very useful.
There are at least two ways to construct them:
1) constructor
2) "Builder" (It is not Builder pattern, it is like
http://code.google.com/intl/cs/apis/protocolbuffers/docs/javatutorial.html#builders
)
What about generating Builders. I've some ideas about the API.

Reinier Zwitserloot

unread,
Aug 3, 2009, 1:20:23 PM8/3/09
to Project Lombok
I'm not quite sure what the builder pattern is, if that isn't it. It's
in the planning. Our current rough sketch is very similar to the link
you posted. The major hurdles, design wise, is flexibility: How would
you make small modifications to the generated builder? Modifications
like:

- Put a few of the fields in the 'newBuilder()' method - this is
sometimes popular for final fields, especially if you only have one or
two of those and a bazillion others.
- Set defaults for some of the values if you forgot them (especially
useful for final fields, as the builder can't create new objects
without values for those).
- add custom methods to the builder.
- adding a non-standard constructor to the main class. How does the
builder know what's what?


There are reasonable answers for almost all of them (including: "Can't
do that - if you want that, write it out in full without the help of
lombok", but answering that to ALL of those is probably going
overboard. A few potential ideas here:

- A per-field annotation listing the default and listing if its to be
included in the newBuilder() construction. On the flipside, that's
rather a lot of annotating.
- Don't annotate the main class with @Builder, but any other class
with @Builder(for=MainClass.class), defaulting to the containing class
if you're an inner class. This way, you can just add custom methods
there.
- crazy plan: Don't add a Builder at all, just modify @Data to be
capable of generating "SelfType fieldName(param)" instead of "void
setFieldName(param)" style setters. This way you can easily create the
build() method on your own, and you've got plenty of flexibility,
though you'd have to duplicate every variable of the class you're
building for, so it doesn't remove as much boilerplate as you want.
- crazy plan #2: I'm not quite sure how yet, but we'd love to add
some sort of named parameter scheme to lombok. Once you have that, the
entire builder pattern becomes a lot less useful.

On Aug 3, 6:45 pm, v6ak <vit...@gmail.com> wrote:
> Immutable classes are very useful.
> There are at least two ways to construct them:
> 1) constructor
> 2) "Builder" (It is not Builder pattern, it is likehttp://code.google.com/intl/cs/apis/protocolbuffers/docs/javatutorial...

v6ak

unread,
Aug 4, 2009, 4:20:06 AM8/4/09
to Project Lombok
For example: I have an immutable cass Person.
The Builder the class can be named: PersonBuilder, Person.Builder or
Person.PersonBuilder. It depends on conventions. I'll use
PersonBuilder there.
Instantiation: there are some possibilities:
1. PersonBuilder can instantiate Person by a build() method. However,
I can make Person from PersonBuilder by constructor (new PersonBuilder
(aPerson)) or a static method (PersonBuilder.createFromPerson
(aPerson)). *So Person does not depend on its Builder.*
2. Person can instantiate PersonBuilder by a createBuilder() method.
However, PersonBuilder can instantiate Person by new Person
(aPersonBuilder) or Person.createFromBuilder(aPersonBuilder). It's
really crazy, I think.
3. Person can instantiate PersonBuilder by a createBuilder() method
and PersonBuilder can instantiate Person by a build() method.

There is my immutable class Person:
import lombok.Data;
public final @Data class Person {
private final String firstName;
private final String lastName;
private final Email email;
}

Now I'd like to create the PersonBuilder.

Independent version (first possibility - Person does not depend on the
Builder):
import lombok.BuilderFor;
@BuilderFor(Person.class) // Class Person has exactly one constructor.
Otherwise it causes an error.
public class PersonBuilderWithIndependentPerson {
}

I can specify the constructor:
import lombok.BuilderFor;
@BuilderFor(Person.class, method="Person
(String,String,com.somewhere.Email)")
public class PersonBuilderWithIndependentPerson {
}

If I want to use a static factory, not a constructor.
import lombok.BuilderFor;
@BuilderFor(forClass=PersonFactory.class, method="createPerson") //
Class Person has exactly one createPerson method. Otherwise it causes
an error.
public class PersonBuilderWithIndependentPerson {
}

I can specify the method:
import lombok.BuilderFor;
@BuilderFor(forClass=PersonFactory.class, method="createPerson(String,
String)") // e-mail is generated, loaded or null - it depends on
PersonFactory.createPerson(String, String)
public class PersonBuilderWithIndependentPerson {
}

There is an advantage: I can add custom methods to my Builder (e.g.
public void setEmail(String email){ setEmail(new Email
(email)); }).

There is one issue: it is neccessary to leave parameter names in
compiled code, I think. (I currently don't fully understand how do
these compile-time annotations work. So I am maybe wrong.)

Note that I've low experience with annotations, so you maybe know
better syntax (e.g. better syntax for method/constructor links).

You can also use third possibility (Person.createBuilder() and
PersonBuilder.build()):

import lombok.Data;
import lombok.Buildable;
public final @Buildable @Data class Person {// PersonBuilder class
will be created.
private final String firstName;
private final String lastName;
private final Email email;
}

It is also useful to can determine the name:
import lombok.Data;
import lombok.Buildable;
public final @Buildable(name="SpecialPersonBuilderName") @Data class
Person { // SpecialPersonBuilderName will be created.
private final String firstName;
private final String lastName;
private final Email email;
}

I think that @Buildable+@Data should use the constructor generated by
@Data. If you'd like to use another constructor, you must annotate the
constructor. It is simple, isnť it? However, it is not possible to use
more @Buildable annotations per class because of the createBuilder()
method.

Well, I'd like to have a custom methods in my PersonBuilder:
import lombok.Data;
import lombok.BuildableBy;
public final @BuildableBy(PersonBuilder.class) @Data class Person {//
@BuildableBy creates createBuilder() method and checks whether
PersonBuilder is Builder for this class.
private final String firstName;
private final String lastName;
private final Email email;
}

Now I have to create the PersonBuilder class:
import lombok.Data;

@BuilderForBuildable(Person.class)// this will also cause check that
Person or exactly one of its constructor is @BuildableBy
(PersonBuilder.class).
public class PersonBuilder {

{
// default values:
setFirstName("John");// it is null if not set
setLastName("Smith");// it is null if not set
// default value of email is null
}

public void setEmail(String email) {
setEmail(new Email(email));
}

}

Default values are also solved.

Note that I prefer name createBuilder() to newBuilder() because of the
verb.

Ad crazyPlan#2: I don't understand.

Reinier Zwitserloot

unread,
Aug 4, 2009, 7:25:55 AM8/4/09
to Project Lombok
Putting field names in string literals is ugly, but we've done it for
ToString's and EqualsAndHashCode's 'exclude' parameter. But sticking
entire method signatures in there is a bridge too far for me. Putting
code in string literals strikes me as extremely non-elegant.

If there IS a builder, then clearly there is no need for a complicated
construction mechanism (such as 'static constructors') in the mean
class - that's just adding needless complication to support redundant
API (and redundant API is a bad thing. There should be One Way To Do
It, not many). Therefore, @Builder will not support specifying a
static method. We'll be sure to post a proposal here before building
it to gather comments first.

v6ak

unread,
Aug 4, 2009, 7:43:00 AM8/4/09
to Project Lombok
Ad "static constructors": See
http://blog.codefront.net/2003/06/21/java-tip-2-static-factory-methods-vs-constructors/
Ad field names/method names/method signatures in literals: I agree
that it is ugly, but I don't know better way. BTW: If it is
interpreted in compile-time, it is not so ugly.
There are many suggested ways to do it and the "ugly things" are as
special case. (special case = you don't want the default behavior)
Reply all
Reply to author
Forward
0 new messages