代码改变世界

遇到构造器中有多个可选参数时要考虑用构建器

2017-03-01 23:05  ttylinux  阅读(348)  评论(0编辑  收藏  举报
最近重新阅读《Effectiva Java》,以前是刚写程序不久的时候阅读,读的的时候迷迷糊糊。现在,比以前好多了,读了,明白它的含义。
 
本文涉及的概念:
a.重叠构造器
b.JavaBean
c.Builder构建器模式
d.对象状态一致性,唯一性;线程安全
 
场景:
1.一个类,有过个构造器,每个构造器的参数数量和顺序是不同的;该构造器有必须的参数
实现这种需求,有以下两种方式:
a.使用重叠构造器方式:
 
public class FoodTelescopingDemo {

  private final int id;
  private final String name;
  private final int calories;
  private final int servingSize;
  private final int fat;
  private final String description;

  public FoodTelescopingDemo(int id, String name) {
    this(id, name, 0, 0, 0, "default description");
  }

  public FoodTelescopingDemo(int id, String name, int calories) {
    this(id, name, calories, 0, 0, "default description");
  }

  public FoodTelescopingDemo(int id, String name, int calories, int servingSize) {
    this(id, name, calories, servingSize, 0, "default description");
  }

  public FoodTelescopingDemo(int id, String name, int calories, int servingSize, int fat) {
    this(id, name, calories, servingSize, fat, "default description");
  }

  /**
   * Main Constructor - actually builds the object
   */
  public FoodTelescopingDemo(int id, String name, int calories, int servingSize, int fat,
      String description) {
    this.id = id;
    this.name = name;
    this.calories = calories;
    this.servingSize = servingSize;
    this.fat = fat;
    this.description = description;
  }

  public int getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public int getCalories() {
    return calories;
  }

  public int getServingSize() {
    return servingSize;
  }

  public int getFat() {
    return fat;
  }

  public String getDescription() {
    return description;
  }

}
 
    // Use of the Telescoping Constructor
    FoodTelescopingDemo telescopingConstructor1 = new FoodTelescopingDemo(1,
        "My Name");
    // Use the new object
    telescopingConstructor1.toString();
    
    FoodTelescopingDemo telescopingConstructor2 = new FoodTelescopingDemo(1,
        "My Name", 4, 3);
    // Use the new object
    telescopingConstructor2.toString();
    
    FoodTelescopingDemo telescopingConstructor3 = new FoodTelescopingDemo(1,
        "My Name", 4, 3, 34);
    // Use the new object
    telescopingConstructor3.toString();
 

 

 
缺点:缺点就是,你在写代码的时候,写构造器的时候,你会很痛苦,你要记住参数的顺序,不能搞错;你要添加多个只是指定了默认参数的构造器。其它人在使用该类的构造器进行实例化的时候,如果选择只需要两个参数的构造器还好,如果选择参数数量大于3个的构造器,那就很痛苦,容易出错。要排好序,记住顺序。
 
b.使用JavaBeans的方式来实现:
 
public class FoodBean {
private int id;
private String name;
private int calories;
private int servingSize;
private int fat;
private String description;
 
public FoodBean(){
 
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setCalories(int calories) {
this.calories = calories;
}
public void setServingSize(int servingSize) {
this.servingSize = servingSize;
}
public void setFat(int fat) {
this.fat = fat;
}
public void setDescription(String description) {
this.description = description;
}
 
public static void main(String[] args){
FoodBean bean = new FoodBean();
bean.setId(10);
bean.setName("Apple");
 
}
}
 

 

 
 
缺点:对象的构造过程被分割到了几个调用中,这个对象的状态依赖于set方法的调用。这会出现什么问题?如果把这块代码:
 
FoodBean bean = new FoodBean();
bean.setId(10);
bean.setName("Apple");

 

 
添加到同步语句块,确保某一个时刻只有一个线程进行对象构造,那么就没问题。否则,会出现对象的状态不一致。比如,其它线程访问该对象,而该对象的构造过程还没有走完(依赖于set方法的执行)。
这是最主要的问题,这个问题在线程同步时,容易出现。这样的对象,是线程不安全的。
另外的缺点,“JavaBeans模式阻止了把类做成不可变的可能”。
 
 
c.使用构建器来实现:
 
public class Food {
 
private final int id;
private final String name;
private final int calories;
private final int servingSize;
private final int fat;
private final String description;
 
public static class Builder {
 
private int id;
private String name;
private int calories;
private int servingSize;
private int fat;
private String description;
 
public Builder(int id, String name) {
this.id = id;
this.name = name;
}
 
public Builder Calories(int calories) {
this.calories = calories;
return this;
}
 
public Builder ServingSize(int servingSize) {
this.servingSize = servingSize;
return this;
}
 
public Builder Fat(int fat) {
this.fat = fat;
return this;
 
}
 
public Builder Description(String description) {
this.description = description;
return this;
}
 
public Food build() {
return new Food(this);
}
}
 
private Food(Builder builder) {
id = builder.id;
name = builder.name;
calories = builder.calories;
servingSize = builder.servingSize;
fat = builder.fat;
description = builder.description;
}
 
public static void main(String[] args) {
 
Food food = new Food.Builder(10, "Apple").Description("An Apple").ServingSize(20).build();
 
}
 
}
 

 

缺点:为了要创建一个类的实例,要先创建一个构建器对象(Builder实例)。
优点:在存在多个构造器,每个构造器的参数都比较多,客户端在创建某个构造器时,需要按顺序指定多个参数时,在这样的场景下,可以使用Builder模式。用户在使用Builder创建对象时,会更轻松,构造器参数的语意明确,不用记住顺序。容易阅读,容易写。