静态工厂和构造器有个共同的局限性:它们都不能很好的扩展到大量的可选参数。
考虑用一个类表示食品的营养成分标签,这些中有几个是必须的:每份的含量,每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。
有三种方式来编写这样的类:
- 重叠构造器模式
- JavaBean模式
- Builder模式
下面分别介绍
重叠构造器模式
在这种模式下,你提供一个只有必要参数的构造器。第二个构造器有一个可选参数,第三个有两个可选参数,以此类推,最后一个构造器包含所有可选参数,示例如下:
|
|
重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然比较难以阅读。一长串类型相同的参数会导致一些微妙的错误,并且很难发现。
JavaBean模式
遇到许多构造器参数的时候,还有第二种代替办法,即JavaBean模式,在这种模式下,调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数:
|
|
这种模式弥补了重叠构造器模式的不足,创建实例很容易,这样产生的代码阅读起来也很容易。但是JavaBean模式自身有着很严重的缺点。因为构造过程被分配到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的 有效性来保持一致性。试图使用处于不一致状态的对象,将会导致失败,这种失败与饱含错误的代码大相径庭,因此调试起来十分困难。于此相关的另一点不足在于,JavaBean模式阻止了把类做成不可变的可能。
当对象的构造完成,并且不允许在解冻之前使用时,通过手工冻结对象可以弥补这些不足,但是这种方式十分笨拙,在实际中很少使用。
Builder模式
不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端嗲用无参的build方法来生成不可变的对象。这个builder是它构建的类的静态成员类。示例如下:
|
|
NutritionFacts是不可变的,所有的默认参数值都单独放在一个地方。builder的setter方法返回builder本身,以便可以把调用链接起来。
Builder模式也有不足,为了创建对象,必须先创建它的构建器。在十分注重性能的情况下,就可能成问题。Builder模式比重叠构造器模式更加冗长,因此只有在有很多参数的时候使用,比如4个或者更多。
简而言之,如果一个类的构造器活着静态工厂中具有多个参数,设计这种类时,Builder模式就是一种不错的选择。