11

Suppose you create a class names Person using the builder pattern, and suppose the Builder class contains methods body(), head(), arms() and of course build() and you consider methods head() and build() obligatory for the user of this class.

We would like to somehow mark these methods obligatory, if possible using annotations. If a user of this class tries to build a Person instance but forgot to call either of these methods, we would like to get some kind of warning - either from the java compiler, or maybe from Eclipse or Maven, which we use to build our projects - any of them would do.

Is it possible to do? Which way would you suggest?

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
uzilan
  • 2,554
  • 2
  • 31
  • 46
  • Doubt you can do this at compile time, except in *very* special cases. Should be pretty easy to have checks like this at run time though (I do this all the time). – NPE Feb 03 '12 at 09:22
  • 1
    I can't imagine a way to do this other then adding mandatory properties as arguments to the builder constructor or throw an exception when `build` is called, but I'm curious if somebody has a better idea. – Michael Schmeißer Feb 03 '12 at 09:24

6 Answers6

26

Here is an example with using different types to make some parts mandatory (it also makes the order you call the methods mandatory):

package test;

import test.StepOne.StepThree;
import test.StepOne.StepTwo;
import test.StepOne.LastStep;

public class TestBuilder {

    public static void main(String[] args) {

        String person1 = PersonBuilder.newInstance().head("head").body("body").arm("arm").leg("leg").build();

        String person2 = PersonBuilder.newInstance().head("head").body("body").arm("arm").build();

    }

}

interface StepOne {

    // mandatory
    StepTwo head(String head);

    interface StepTwo {
        // mandatory
        StepThree body(String body);
    }

    interface StepThree {
        // mandatory
        LastStep arm(String arm);
    }

    // all methods in this interface are not mandatory
    interface LastStep {
        LastStep leg(String leg);
        String build();
    }

}

class PersonBuilder implements StepOne, StepTwo, StepThree, LastStep {

    String head;
    String body;
    String arm;
    String leg;

    static StepOne newInstance() {
        return new PersonBuilder();
    }

    private PersonBuilder() {
    }



    public StepTwo head(String head) {
        this.head = head;
        return this;
    }

    public LastStep arm(String arm) {
        this.arm = arm;
        return this;
    }

    public StepThree body(String body) {
        this.body = body;
        return this;
    }

    public LastStep leg(String leg) {
        this.leg = leg;
        return this;
    }

    public String build() {
        return head + body + arm + leg;
    }
}


Edit

The OP was so impressed with this answer that he wrote it up fully in a blog. It's such a clever take on the builder pattern that a full treatment deserves to be referenced here.

Software Engineer
  • 15,457
  • 7
  • 74
  • 102
pgras
  • 12,614
  • 4
  • 38
  • 46
  • That's a very cool approach! But can it be done without forcing the order, i. e. with interfaces like Mandatory and NonMandatory? – uzilan Feb 03 '12 at 11:29
  • No you have to force the order because in every interface you need a method that returns the next step, so as soon as you put several methods in one of the interfaces you will force only to call one of them to be able to navigate to the next step... – pgras Feb 03 '12 at 11:49
  • But you could add to the interfaces methods that do not add the property (e.g. `PersonBuilder.newInstance().head("head").doesntHaveABody().arm("arm")` – yannick1976 Oct 22 '15 at 10:45
3

I believe the correct use of the builder pattern would solve the issue you're having.

I would create class PersonBuilder which would contain the methods setBody() and setArms() and every other optional parameter setter method. The constructor of the builder would take the required parameters. Then the method build() would return the new instance of Person.

public class PersonBuilder
{
    private final Head head;
    private Body body;
    private Arms arms;

    public PersonBuilder(Head head)
    {
        this.head = head;
    }

    public void setBody(Body body)
    {
        this.body = body;
    }

    public void setArms(Arms arms)
    {
        this.arms = arms;
    }

    public Person build()
    {
        return new Person(head, body, arms);
    }
}

Alternatively you could pass the Head parameter to the method build() but I prefer passing it in the constructor instead.

janhink
  • 4,943
  • 3
  • 29
  • 37
2

No way with the compiler.

You can do is throw a runtime exception from the build() method that the builder is not properly initialized (and have a test that is invoked in the maven test phase)

But you can also have build(..) accept a HeadDetails object. That way tou can't invoke build without specifying the obligatory parameters.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
1

Why not calling body(), head(), arms() in the build()-Method if it is really mandatory and returning Person in the build() method?

[edit]

Short example:

public class Builder {

private final String bodyProp;

private final String headProp;

private final String armsProp;

private String hearProps;

public Builder(String bodyProp, String headProp, String armsProp) {
    super();
    this.bodyProp = bodyProp; // check preconditions here (eg not null)
    this.headProp = headProp;
    this.armsProp = armsProp;
}

public void addOptionalHair(String hearProps) {
    this.hearProps = hearProps;
}

public Person build() {
    Person person = new Person();

    person.setBody(buildBody());
    // ...

    return person;
}



private Body buildBody() {
    // do something with bodyProp
    return new Body();
}


public static class Person {

    public void setBody(Body buildBody) {
        // ...
    }
}

public static class Body {
}
}
ollins
  • 1,851
  • 12
  • 16
  • btw. if body, head and arms need some parameters and they are also mandatory then put them in the constructor of the builder. – ollins Feb 03 '12 at 09:58
  • These methods require arguments which I'd have to send to the build() method. This is against the builder pattern as I understand it. – uzilan Feb 03 '12 at 10:17
0

Maybe inside of build() you could check if all the required methods have been called. Behaps the Person instance has some internal sanity check which is triggered by build().

Of course this checks runtime behaviour and is no static analysis as you describe it.

0

isn't possible to call these methods in Person's constructor ?

tartak
  • 485
  • 3
  • 17
  • 1
    One of the ideas with the builder pattern is that the user can choose which methods in Builder she would like to call. Another is to avoid constructors with many arguments and many overloads (which cover all the possible ways to create this object). I'm afraid what you suggest might break these benefits. We would like to retain the user's freedom to create the Person object with any methods they need, but at the same time assert that some obligatory methods are called, preferably in build time. – uzilan Feb 03 '12 at 10:00