美文网首页
Effective Java - 遇到多个构造器参数时要考虑用B

Effective Java - 遇到多个构造器参数时要考虑用B

作者: DZQANN | 来源:发表于2022-04-28 17:27 被阅读0次

Builder模式是为了应对大量的可选参数的情况。

Builder模式的优势

  1. 相比于set方法,builder模式可以让所有的属性都是final的,只能在build的时候初始化一次
  2. 和重载多个构造器相比,代码的可读性很高
  3. 可以有多个可变参数
  4. 易于做参数检查和构造约束检查
  5. 灵活性: 可以利用单个builder构建多个对象
  6. 可以自动填充某些域, 比如自增序列号

对于Builder模式中必填字段,当必填字段比较多的时候,也可以不把这些字段放到构造器里,在build的时候做一次校验,如果缺失了必填字段则抛Exception。把编译限制退化成运行时的限制。

Builder模式的使用

这是是书上的一个例子:

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;
    }
}

Builder模式在很多框架中都有体现,比如Spring Security

protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/templates/*.html").permitAll()
                .antMatchers("/templates/member/**").hasRole("memmber") 
                .antMatchers("/templates/employee/**").hasRole("admin")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .permitAll()
                .and()
                .logout()
                .permitAll();
}

Builder模式继承

Builder模式也可以适用于继承结构,这里就会用到"递归类型参数"的内容。书中也举了一个例子,逻辑有点绕,不过能看懂个大概。

这方面使用场景不多,简单看看就可以,如果真的使用到了也可以直接套作者的例子

public abstract class Pizza {
    public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}

    final Set<Topping> toppings;

    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Topping> 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
    }
}


public class NyPizza extends Pizza {
    public enum Size {SMALL, MEDIUM, LARGE}

    private final Size size;

    public static class Builder extends Pizza.Builder<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;
    }

    @Override
    public String toString() {
        return "New York Pizza with " + toppings;
    }
}

public class Calzone extends Pizza {
    private final boolean sauceInside;

    public static class Builder extends Pizza.Builder<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;
    }

    @Override
    public String toString() {
        return String.format("Calzone with %s and sauce on the %s",
                toppings, sauceInside ? "inside" : "outside");
    }
}

TMS里使用场景无所不在,大多数VO都有十几、几十个字段,使用builder相对比较合适一点

相关文章

网友评论

      本文标题:Effective Java - 遇到多个构造器参数时要考虑用B

      本文链接:https://www.haomeiwen.com/subject/zjkdyrtx.html