Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java BuilderTestPattern - how to avoid boilerplate?

I have a lot of value objects in my project.

I'm using project lombok to eliminate some boilerplate, so my value objects look like the following one:

@Value
@Accessors(fluent = true)
public class ValueObject {

    private final String firstProp;
    private final int secondProp;   

}

Not bad, almost no boilerplate.

And now, I'm using the all-args constructor quite often in my tests. It looks quite messy, so I thought I will introduce Builder Pattern variant instead:

public class ValueObjectBuilder {

    private static final int DEFAULT_VALUE_FOR_SECOND_PROP = 666;

    private String firstProp = "default value for first prop;
    private int secondProp = DEFAULT_VALUE_FOR_SECOND_PROP;

    private ValueObjectBuilder() {}

    public static ValueObjectBuilder newValueObject() {
        return new ValueObjectBuilder();
    }

    public ValueObjectBuilder withFirstProp(String firstProp) {
        this.firstProp = firstProp
        return this;
    }

    public ValueObjectBuilder withFirstProp(int secondProp) {
        this.secondProp = secondProp;
        return this;
    }

    public ValueObject build() {
        return new ValueObject(
                firstProp, secondProp
        );
    }
}

and the code looks quite nice now:

ValueObjectBuilder
.newValueObject()
.withFirstProp("prop")
.withSecondProp(15)
.build();

Now, the problem is - as I mentioned, I have to write a lot of similar classes... I'm already tired with copy-paste'ing them.

What I'm looking for, is a black-magic-smart-tool, which will somehow generate this code for me.

I know, there is a @Builder annotation in Lombok, but it doesn't meet my requirements. Here's why:

1) I'm unable to provide default values in lombok's Builder. Well, actually, it is possible - by implementing builder class template myself like

@Builder
public class Foo {
    private String prop;

    public static class FooBuilder() {
        private String prop = "def value";
        ...
    }

}

which generates some boilerplate too.

2) I can't find any way to put prefix on each field accessor in lombok's builder. Maybe @Wither could help here? But I don't know, how to use it properly.

3) The most important reason: I'm not creating a "natural" builder. As far as I understand, lombok is designed to create Builder for a given, annotated class - I don't know if there is a way to return any other object from within build() method.

So, to sum up: Do you know any tool which could possibly help me? Or maybe all those things I mentioned are in fact possible to achieve using Lombok?

EDIT

Ok, so I probably found a solution to this particular case. With lombok we can use:

@Setter
@Accessors(chain = true, fluent = true)
@NoArgsConstructor(staticName = "newValueObject")
public class ValueObjectBuilder {
    private String firstProp = "default";
    private int secondProp = 666;

    public ValueObject build() {
        return new ValueObject(firstProp, secondProp);
    }
}

Cheers, Slawek

like image 789
slnowak Avatar asked Jan 09 '15 19:01

slnowak


2 Answers

I know this is old but if anyone else runs into this, I found an alternative solution for providing default values for a builder.

Override the builder method and provide the default values before returning a builder. So in the above case:

@Builder
public class Foo {
    private String prop;

    public static FooBuilder builder() {
        return new FooBuilder().prop("def value");
    }
}

It's not an ideal solution but beats having to override the whole builder itself or have a custom constructor (which is painful IMHO if there are a lot of variables. It would still be nice to have something along the lines of a @With or @Default annotation to handle this.

like image 148
mikea Avatar answered Nov 07 '22 06:11

mikea


Try Bob-the-builder for eclipse. Hmm.. I guess that works best if you happen to be using eclipse! If you are not using eclipse, there are a few related projects mentioned at the bottom of the page linked here that may be useful.

like image 24
unigeek Avatar answered Nov 07 '22 07:11

unigeek