摘要:Effective-Java
第二条-遇到多个构造器参数时要考虑用构建器
Item
ITEM 2: CONSIDER A BUILDER WHEN FACED WITH MANY CONSTRUCTOR PARAMETERS
遇到多个构造器参数时要考虑用构建器
解释
参数较多的构造函数的解决方法1
telescoping constructor
(重叠构造器): 就是通过不同的参数构造器来实现不同的可选参数构造方法。但是一旦参数增加,构造器的数量会大幅度增加。javabeans pattern
:使用setter
来设置对象的属性。导致对象的不一致。
这时候我们可以考虑使用构造器模式(Build Pattern
)来解决这个问题. 对于该模式的实现,后面和设计模式对比中详细描述。
缺点
- 构造器本身有开销,因为是另外一个类
- 代码更加冗长
使用注意点
- 如果类的构造器或者錚态工厂中具有多个参数,设计这种类时,
Builder
模式就是种不错的选择,特别是当大多数参数都是可选的时候。 - 如果使用
Builder
,最好一开始就是用。
关于该模式和生成器设计模式的区别
通过 Effective Java 2 中的 Builder Pattern
(以下我们简称为 BP)和设计模式中的 Builder Design Pattern
(一下我们简称为 BDP)进行对比。我们可以总结一下内容,方便今后开发中灵活使用。
相似之处
- 两种模式的目标就是在客户端更加清晰简洁实例化类。传统的实例化方法可以使用构造函数,通过传入参数来实现,也可以先实例化空的对象,再使用
setter
进行赋值。但是这两种方式有很大的缺点,增加了客户端的工作量,同时增加了出错的概率。 - 两种模式都利用另外一个类来与客户端交互。BP 是使用了内部静态类
Builder
,BDP 是使用了一个抽象类Builder
。 - 两种模式都可以构造不同默认值的对象,但是方法不同。BP 通过在
Builder
中创建新的方法来实现,而 BDP 通过创建新的Concrete Builder
继承并实现其方法来实现。
不同之处
- BP 一般要求目标对象(也可以成为产品)为成员变量为
final
,及该对象被 build 出来后是不能变的。 - 两者把
builder
转化为对象的过程逻辑是不一样的,BP 由于目标对象的成员变量一般为final
,所以把builder
转化为对象一般通过构造函数,直接将builder
值赋给对象的成员变量。而 BDP 在builder
中有一个专门的方法来创建对象,也就是依靠目标对象提供的方法,创建目标对象。最后由Director
来调用这个方法完成目标对象的创建。可以说 BDP 能够更加灵活的构造对象。
应用场景
简单的说, BP 更适合逻辑简单的目标对象的构造,而 BDP 更适合复杂产品的构造,同时 BDP 适合典型的生产不同类型的产品。比如生产汽车,有很多型号,但是整体汽车的配件基本一致,只是配件的型号可能不同。而开发中可能有些类虽然不复杂,但是有非常多的参数,而需要灵活的创建。
两种模式的实现方法总结
Builder Pattern
- 目标对象成员变量为
final
,构造函数通过Builder
传递所有的成员变量 - 包含一个内部的静态类
Builder
,其中成员变量和目标对象一致,不过如果成员变量是optional
的,不能加final
。 Builder
有一个构造函数,该构造函数传入必须的成员变量(带final
的)。其他可选成员变量可以任意组合创建函数来实现类似于setter
的方法,但是方法名称和参数可以根据实际需求进行组合。最灵活的方式是每个可选参数都对应一个方法来设置。这些方法需要返回Builder
,也就是本身this
。Builder
中需要有一个方法来返回目标对象,实际上就是使用目标对象的构造函数,然后传入Builder
本身并实例化得到。
step by step:查看代码中的注释,代码可以查看 Github 仓库
1 | public class NutritionFactsBuilder { |
Builder Design Pattern
- 目标对象
Product
- 抽象构造器
Abstract Builder
Director
Concrete Builder
- Client Usage
enter code here
类图:
Product
1 | // This class is "Product", the target we need to build. |
Abstract Builder
1 | /** |
Concrete Builbder
1 |
|
Director
1 | /** |
Client Example
1 | public class BuilderExample { |