静态工厂和构造器有个共同的局限性:都不能很好地扩展大量的可选参数
例:
考虑用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必需的:每份含量,每罐含量,每份卡路里,还有超过20个可选域:总脂肪量,饱和脂肪量,转化脂肪量,胆固醇,钠等等。大多数的产品在某几个可选域中都会有非0的值。
对于这样的类,应该用哪种构造器或者静态方法来编写呢?
1. 重叠构造器 (telescoping constructor)
提供第一个只有必要参数的构造器
提供第二个包含必要参数且包含一个可选参数的构造器
提供第三个包含必要参数且包含两个可选参数的构造器
以此类推,最后一个构造器包含所有参数
如下是个简单的示例
1 | public class NutritionFacts { |
仅仅想创建一个该类对象,使用最短的构造器即可
但如果想要设置参数表中靠后的参数问题就来了
1 | NutritionFacts cocaCola = new NutritionFacts(240,8,100,0,35,27); |
如上初始化中 fat 的值为0,这个参数本是不用初始化的,就6个参数的情况下,还说的过去,随着参数增加,这样就不行了。
重叠构造器是可行的,但当参数增多时,客户端的代码将变得很难编写,阅读性也较差,并且如果初始化时容易填错顺序,导致运行时的错误。
2. JavaBeans模式
1 | // JavaBeans Pattern - allows inconsistency, mandates mutability |
JavaBeans模式创建实例很容易,代码可读性也很强,但其有很严重的缺点:
构造的过程包含多个调用,在构造过程中JavaBeans可能处于不一致状态
例如一个线程正在使用setter初始化值,而另一个线程正用getter取得该对象的字段
使用JavaBeans模式则该类不可成为不可变类 (因为有setter访问器)
3. Builder模式
此模式有重叠构造器的安全性,也有JavaBeans模式的高可读性
不直接生成需要的对象,而是得到一个builder对象,调用所有的必要的构造器或静态工厂,设置每一个需要设置的参数,最后调用一个无参数的build方法来生成一个不可变对象。
1 | public class NutritionFacts { |
Builder 模式的可读性提高了很多,代码简易。Builder模式模拟了了具名可选参数
如果该类包含多个字段,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候。与重叠构造器与JavaBeans相比要更加安全与易读。