How to improve the builder pattern?

后端 未结 10 873
野性不改
野性不改 2020-12-02 10:34

Motivation

Recently I searched for a way to initialize a complex object without passing a lot of parameter to the constructor. I tried it with the builder pattern,

相关标签:
10条回答
  • 2020-12-02 11:03

    For more information on when to use the Builder Pattern and its advantages you should check out my post for another similar question here

    0 讨论(0)
  • 2020-12-02 11:08
    public class Complex {
        private final String first;
        private final String second;
        private final String third;
    
        public static class False {}
        public static class True {}
    
        public static class Builder<Has1,Has2,Has3> {
            private String first;
            private String second;
            private String third;
    
            private Builder() {}
    
            public static Builder<False,False,False> create() {
                return new Builder<>();
            }
    
            public Builder<True,Has2,Has3> setFirst(String first) {
                this.first = first;
                return (Builder<True,Has2,Has3>)this;
            }
    
            public Builder<Has1,True,Has3> setSecond(String second) {
                this.second = second;
                return (Builder<Has1,True,Has3>)this;
            }
    
            public Builder<Has1,Has2,True> setThird(String third) {
                this.third = third;
                return (Builder<Has1,Has2,True>)this;
            }
        }
    
        public Complex(Builder<True,True,True> builder) {
            first = builder.first;
            second = builder.second;
            third = builder.third;
        }
    
        public static void test() {
            // Compile Error!
            Complex c1 = new Complex(Complex.Builder.create().setFirst("1").setSecond("2"));
    
            // Compile Error!
            Complex c2 = new Complex(Complex.Builder.create().setFirst("1").setThird("3"));
    
            // Works!, all params supplied.
            Complex c3 = new Complex(Complex.Builder.create().setFirst("1").setSecond("2").setThird("3"));
        }
    }
    
    0 讨论(0)
  • 2020-12-02 11:12

    IMHO, this seems bloated. If you have to have all the parameters, pass them in the constructor.

    0 讨论(0)
  • 2020-12-02 11:14

    Why don't you put "needed" parameters in the builders constructor?

    public class Complex
    {
    ....
      public static class ComplexBuilder
      {
         // Required parameters
         private final int required;
    
         // Optional parameters
         private int optional = 0;
    
         public ComplexBuilder( int required )
         {
            this.required = required;
         } 
    
         public Builder setOptional(int optional)
         {
            this.optional = optional;
         }
      }
    ...
    }
    

    This pattern is outlined in Effective Java.

    0 讨论(0)
  • 2020-12-02 11:14

    Instead of using multiple classes I would just use one class and multiple interfaces. It enforces your syntax without requiring as much typing. It also allows you to see all related code close together which makes it easier to understand what is going on with your code at a larger level.

    0 讨论(0)
  • 2020-12-02 11:15

    Question 1: Regarding the name of the pattern, I like the name "Step Builder":

    • http://rdafbn.blogspot.com/2012/07/step-builder-pattern_28.html
    • http://www.javacodegeeks.com/2013/05/building-smart-builders.html

    Question 2/3: Regarding pitfalls and recommendations, this feels over complicated for most situations.

    • You are enforcing a sequence in how you use your builder which is unusual in my experience. I could see how this would be important in some cases but I've never needed it. For example, I don't see the need to force a sequence here:

      Person.builder().firstName("John").lastName("Doe").build() Person.builder().lastName("Doe").firstName("John").build()

    • However, many times the builder needed to enforce some constraints to prevent bogus objects from being built. Maybe you want to ensure that all required fields are provided or that combinations of fields are valid. I'm guessing this is the real reason you want to introduce sequencing into the building.

      In this case, I like recommendation of Joshua Bloch to do the validation in the build() method. This helps with cross field validation because everything is available at this point. See this answer: https://softwareengineering.stackexchange.com/a/241320

    In summary, I wouldn't add any complication to the code just because you are worried about "missing" a call to a builder method. In practice, this is easily caught with a test case. Maybe start with a vanilla Builder and then introduce this if you keep getting bitten by missing method calls.

    0 讨论(0)
提交回复
热议问题