02.当构造方法参数过多时使用builder模式

多个参数时使用Builder替代构造方法

原文笔记

假设存在一个对象,在构造这个对象时需要传入多个参数,而且这些参数没有明显的区分特征。在这种情况下,可以有两种实现方法:第一种通过构造方法传入所需的参数;第二种方法可以创建一个空的对象,然后通过调用对象的setter方法设置参数。

针对第一种方法,可能会出现如下的调用代码:

NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);

这里存在两个问题:第一,我们可能不需要一次传入这么多数据。如这里,可能0这个变量不是我们所需要的,但是为了适配方法的参数列表,我们必须为其设置一个无意义的值。第二,针对这里的每一个数据,在调用方看来不能直观的感受每一个数据的含义。如这里假设调用时将前两个参数位置颠倒了,在编译器看来并不会有任何为题,但是实际业务逻辑已经发生了改变。

针对于第二种方法,先创建一个空的对象,再通过调用对象的setter方法设置数据。

public class NutritionFacts {
    // Parameters initialized to default values (if any)
    private int servingSize = -1; // Required; no default value
    private int servings = -1; // Required; no default value
    private int calories = 0;
    private int fat = 0;
    private int sodium = 0;
    private int carbohydrate = 0;
    public NutritionFacts() { }
    // Setters
    public void setServingSize(int val) { servingSize = val; }
    public void setServings(int val) { servings = val; }
    public void setCalories(int val) { calories = val; }
    public void setFat(int val) { fat = val; }
    public void setSodium(int val) { sodium = val; }
    public void setCarbohydrate(int val) { carbohydrate = val; }
}

这种方式可以避免前面参数性质不清晰的问题,且可以灵活的配置所需要的参数。但是这里可能存在另外一个问题,由于setter调用没有强制性,因此可能存在在对象组装成功之前被调用,这种情况下会出现对象的逻辑与预期的逻辑不一致的问题。针对以上两个问题,引入Builder模式。

Builder实现代码如下:

package com.cs.lock;

// Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;
        // Optional parameters - initialized to default values
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

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

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

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

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

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

    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

调用代码:

NutritionFacts nutritionFacts = new NutritionFacts.Builder(240, 8)
        .calories(100).sodium(35).carbohydrate(27).build();

使用抽象类层次结构实现

  • Pizza

    public abstract class Pizza {
      // 配料
      public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}
    
      final Set toppings;
    
      abstract static class Builder> {
          EnumSet toppings = EnumSet.noneOf(Topping.class);
    
          public T addTopping(Topping topping) {
              toppings.add(Objects.requireNonNull(topping));
              return self();
          }
    
          abstract Pizza build();
    
          // Subclasses must override this method to return "this"
          protected abstract T self();
      }
    
      Pizza(Builder builder) {
          toppings = builder.toppings.clone(); // See Item 50
      }
    }
    
  • NyPizza

    public class NyPizza extends Pizza {
      public enum Size {SMALL, MEDIUM, LARGE}
    
      private final Size size;
    
      public static class Builder extends Pizza.Builder {
          private final Size size;
    
          public Builder(Size size) {
              this.size = Objects.requireNonNull(size);
          }
    
          @Override
          public NyPizza build() {
              return new NyPizza(this);
          }
    
          @Override
          protected Builder self() {
              return this;
          }
      }
    
      private NyPizza(Builder builder) {
          super(builder);
          size = builder.size;
      }
    }
  • Calzone

    public class Calzone extends Pizza {
      private final boolean sauceInside;
    
      public static class Builder extends Pizza.Builder {
          private boolean sauceInside = false; // Default
    
          public Builder sauceInside() {
              sauceInside = true;
              return this;
          }
    
          @Override
          public Calzone build() {
              return new Calzone(this);
          }
    
          @Override
          protected Builder self() {
              return this;
          }
      }
    
      private Calzone(Builder builder) {
          super(builder);
          sauceInside = builder.sauceInside;
      }
    }

实现代码

假设需要封装一个请求对象,该请求对象由Header、Body两部分组成。在实现代码中,可以在build方法中针对字段属性进行非空或其他逻辑校验,这里不做实现。使用Builder实现如下:

  • RequestMessageHeader:

    public class RequestMessageHeader {
      private String version;
      private String encoding;
      private String intfCode;
    
      private RequestMessageHeader(Builder builder) {
          this.version = builder.version;
          this.encoding = builder.encoding;
          this.intfCode = builder.intfCode;
      }
    
      public static class Builder{
          private final String intfCode;
    
          private String version;
          private String encoding;
    
          public Builder(String intfCode) {
              this.intfCode = intfCode;
          }
    
          public Builder version(String version) {
              this.version = version;
              return this;
          }
    
          public Builder encoding(String encoding) {
              this.encoding = encoding;
              return this;
          }
    
          public RequestMessageHeader build(){
              return new RequestMessageHeader(this);
          }
    
      }
    }
    
  • RequestMessageBody:

    public class RequestMessageBody {
      private final String body;
    
      public RequestMessageBody(Builder builder) {
          this.body = builder.body;
      }
    
      public static class Builder {
          private final String body;
    
          public Builder(String body) {
              this.body = body;
          }
    
          public RequestMessageBody build() {
              return new RequestMessageBody(this);
          }
      }
    }
  • RequestMessage:

    public class RequestMessage {
      private RequestMessageHeader header;
      private RequestMessageBody body;
    
      private RequestMessage(Builder builder) {
          this.header = builder.header;
          this.body = builder.body;
      }
    
      public static class Builder {
          private final RequestMessageHeader header;
          private final RequestMessageBody body;
    
          public Builder(RequestMessageHeader header, RequestMessageBody body) {
              this.header = header;
              this.body = body;
          }
    
          public RequestMessage build() {
              return new RequestMessage(this);
          }
      }
    }

类似文章