【Effective Java】条2:多构造器参数考虑用构造器

构造函数和静态工厂方法1都不能很好的处理多参数问题。

譬如有一个类表示包装食品外面显示的营养成分标签,这些标签包含必须的成分:每份的含量,每罐的含量以及每份的卡路里,还有一些其他非必须的显示,譬如总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。

public class NutritionFacts {

  /** 必须参数*/
  private int servingSize;

  /** 必须参数*/
  private int servings;

  /** 非必须*/
  private int calories;

  /** 非必须*/
  private int fat;

  /** 非必须*/
  private int sodium;

  /** 非必须*/
  private int carbohydrate;

}

试想一下,构建这个类的构造函数,采用构造器和静态工厂方法都不是很好理解。由于参数太多,你可能会分几个构造函数来构造(重叠构造器),又或者采用JavaBean来构建,又或者采用本文所说的构造器模式。

重叠构造器模式

首先提供个必须参数构成的构造函数,然后每增加一个非必须参数构成一个构造函数。实现如下:

public class NutritionFacts {

  /** 必须参数*/
  private int servingSize;

  /** 必须参数*/
  private int servings;

  /** 非必须*/
  private int calories;

  /** 非必须*/
  private int fat;

  /** 非必须*/
  private int sodium;

  /** 非必须*/
  private int carbohydrate;

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

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

  public NutritionFacts(int servingSize, int servings, int calories, int fat) {
    this.servingSize = servingSize;
    this.servings = servings;
    this.calories = calories;
    this.fat = fat;
  }

  public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
    this.servingSize = servingSize;
    this.servings = servings;
    this.calories = calories;
    this.fat = fat;
    this.sodium = sodium;
  }

  public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium,
      int carbohydrate) {
    this.servingSize = servingSize;
    this.servings = servings;
    this.calories = calories;
    this.fat = fat;
    this.sodium = sodium;
    this.carbohydrate = carbohydrate;
  }
}

缺点也显而易见,当参数足够多的时候,调用方可能需要参照构造函数将参数一一对应。万一构造函数改变了参数的顺序,调用方也不得不进行修改。

JavaBean构造

提供默认的构造函数,然后采用set方法进行设置。代码如下:

public class NutritionFacts {

  /** 必须参数*/
  private int servingSize;

  /** 必须参数*/
  private int servings;

  /** 非必须*/
  private int calories;

  /** 非必须*/
  private int fat;

  /** 非必须*/
  private int sodium;

  /** 非必须*/
  private int carbohydrate;

  //Getter & Setter
}

public class NutritionFactsDemo {

  public static void main(String[] args) {
    NutritionFacts nutritionFacts = new NutritionFacts();
    nutritionFacts.setServings(100);
    nutritionFacts.setServingSize(10);
  }

}

JavaBean自身有严重的缺点。其一,由于对象的构造是分步进行的,导致Bean可能不处于一致性状态;其二,在多线程环境中,开发者需要确保构造对象的线程安全。

构造模式(推荐)

不像前两种方式,构造模式先用必须的参数构建builder对象,然后在builder对象为非必须参数调用类似setter的方法,最后调用无参的build方法来生成对象。

public class NutritionFacts {

  /** 必须参数*/
  private int servingSize;

  /** 必须参数*/
  private int servings;

  /** 非必须*/
  private int calories;

  /** 非必须*/
  private int fat;

  /** 非必须*/
  private int sodium;

  /** 非必须*/
  private int carbohydrate;

  private NutritionFacts(Builder builder) {
    this.servingSize = builder.getServingSize();
    this.servings = builder.getServings();
    this.calories = builder.getCalories();
    this.fat = builder.getFat();
    this.sodium = builder.getSodium();
    this.carbohydrate = builder.getCarbohydrate();
  }


  public static final class Builder {

    /** 必须参数*/
    private int servingSize;

    /** 必须参数*/
    private int servings;

    private int calories;
    private int fat;
    private int sodium;
    private int carbohydrate;

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

    public static Builder newInstance(int servingSize, int servings) {
      return new Builder(servingSize, servings);
    }

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

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

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

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

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

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

    // Getter & Setter

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

构造器模式不是没有缺点,缺点就是多了很多代码。但是对于多参数的构造,特别是实际开发中,还是强烈建议使用构造器模式。

多啰嗦一句,在Intellij idea中可以在插件2中搜索builder,使用此插件可以直接生成构造器模式的代码。

扩展阅读

发布了98 篇原创文章 · 获赞 180 · 访问量 40万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章