Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mark a method obligatory?

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?

like image 727
uzilan Avatar asked Feb 03 '12 09:02

uzilan


4 Answers

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.

like image 186
pgras Avatar answered Oct 27 '22 22:10

pgras


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.

like image 26
janhink Avatar answered Oct 28 '22 00:10

janhink


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.

like image 25
Bozho Avatar answered Oct 28 '22 00:10

Bozho


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 {
}
}
like image 24
ollins Avatar answered Oct 27 '22 23:10

ollins