遇到构造器中有多个可选参数时要考虑用构建器
2017-03-01 23:05 ttylinux 阅读(352) 评论(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创建对象时,会更轻松,构造器参数的语意明确,不用记住顺序。容易阅读,容易写。
版权声明:
作者:ttylinux
本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。