analytically / innerbuilder

IntelliJ IDEA plugin which generates an inner builder class

Home Page:https://plugins.jetbrains.com/plugin/7354-innerbuilder/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add support for required fields

martins1930 opened this issue · comments

Hi!

I think that would be nice to have an option to indicate which are the required fields, so in this way we can check at compile time what are the required fields that I must have to provide when I create a class.

In the two links below you can find examples of what I'm saying:
http://www.jayway.com/2012/02/07/builder-pattern-with-a-twist/
http://stackoverflow.com/questions/1638722/how-to-improve-the-builder-pattern

For example, the plugin could generate some code like this:
(In this pice of code the the port and the url are mandatory fields)

public class Address {
    private String protocol;
    private String url;
    private int port;
    private String path;
    private String description;

    // only builder should be able to create an instance
    private Address(Builder builder) {
        this.protocol = builder.protocol;
        this.url = builder.url;
        this.port = builder.port;
        this.path = builder.path;
        this.description = builder.description;
    }

    public static Url builder() {
        return new Builder();
    }

    public static class Builder implements Url, Port, Build{
        private String protocol;
        private String url;
        private int port;
        private String path;
        private String description;

        /** Mandatory, must be followed by {@link Port#port(int)}  */
        public Port url(String url) {
            this.url = url;
            return this;
        }

        /** Mandatory, must be followed by methods in {@link Build}  */
        public Build port(int port) {
            this.port = port;
            return this;
        }

        /** Non-mandatory, must be followed by methods in {@link Build}  */
        public Build protocol(String protocol) {
            this.protocol = protocol;
            return this;
        }

        /** Non-mandatory, must be followed by methods in {@link Build}  */
        public Build path(String path) {
            this.path = path;
            return this;
        }

        /** Non-mandatory, must be followed by methods in {@link Build}  */
        public Build description(String description) {
            this.description = description;
            return this;
        }

        /** Creates an instance of {@link Address} */
        public Address build() {
            return new Address(this);
        }
    }

    interface Url {
        public Port url(String url);
    }

    interface Port {
        public Build port(int port);
    }

    interface Build {
        public Build protocol(String protocol);
        public Build path(String path);
        public Build description(String description);
        public Address build();
    }

    // TODO: add some get methods here...
}

Regards,
Martín.

+1 :-)

If a field is required, make it final?

Hi!

What do you mean with "make it final" ?

When I say "required field", I say that the field must be set when the object is created, therefore the developer is forced to assign a value for the field.

For example, if we have a Car class with three final fields (id, author and text) with the following builder:

Book.newBuilder()
        .withId(idGenerator.generateId())
        .withAuthor("Joe")
        .withText("text of the book").build();

With the typical builder pattern we can forgot one field and create a Car in this way:

// oops I forgot to provide a value for the author field. 
Book.newBuilder()
        .withId(idGenerator.generateId())
        .withText("text of the book").build();

So in this example all fields are final, but I forgot to provide a value for the author field.

I think that is nice to have some kind of builder generator that follow the idea of the links that I pasted in my first comment.

Thanks,
Martín.

Try making the field final and then generate the builder.

I generated the builder with a Book class that has three final fields (id, author and text),
and doesn't work because an object of the Book class can be created without specify a value for id, author and text.
Like I said before:
"When I say "required field", I say that the field must be set when the object is created, therefore the developer is forced to assign a value for the field."

This is the Book class with three final fields (please take a look to the main method that has an example of a creation of a Book object without providing a value for the fields):

// please check the main method, that has an example of a creation of a Book object without 
// providing a value for the fields.
public class Book {

  private final int id;
  private final String author;
  private final String text;

  private Book(Builder builder) {
    id = builder.id;
    author = builder.author;
    text = builder.text;
  }

  public static Builder newBuilder() {
    return new Builder();
  }


  public static final class Builder {

    private int id;
    private String author;
    private String text;

    private Builder() {
    }

    public Builder withId(int val) {
      id = val;
      return this;
    }

    public Builder withAuthor(String val) {
      author = val;
      return this;
    }

    public Builder withText(String val) {
      text = val;
      return this;
    }

    public Book build() {
      return new Book(this);
    }
  }

  public static void main(String[] args) {
    // This Book object is created without specifying a value for the fields. 
    Book book = Book.newBuilder().build();
  }
}

Did you read the links that I put in my first comment?
http://www.jayway.com/2012/02/07/builder-pattern-with-a-twist/
http://stackoverflow.com/questions/1638722/how-to-improve-the-builder-pattern

Very old thread I am putting back to life now.
I face a similar issue.
Final is just good hygiene making your objects immutable. Have nothing to do with required or not.

It would be nice to have the optional to do @NotNull on the required fields and then have the plugin generate validators

We'd also love this! Making a comment here instead of creating a duplicate issue:

STEPS TO REPRODUCE
Call final builder step (usually .build()) without providing required arguments

EXPECTED RESULT
Compiletime error

ACTUAL RESULT
Runtime error

MISC
Instead of returning the builder on all steps, return the next required argument. When there are no required arguments left, return the builder. See eg http://blog.crisp.se/2013/10/09/perlundholm/another-builder-pattern-for-java