构建器
2020-01-25 本文已影响0人
睦月MTK
statement:本篇内容只是建立在我目前经验的基础之上,必然有不完善甚至是不正确的地方,请谨慎阅读,如果能指出错误与不足之处,更是不甚感激
一、问题引入与分析
- 问题引入:假设目前有一个
BuilderTest
类,该类中有多达20
个域,分别名为field1
、field2
...field20
,其中field1
、field2
是必选域,其他18
个是可选域,那么该如何去设计这个类呢? - 问题分析:你可能会自然的想到使用一个包含
field1
与field2
两个类型的参数的构造器,加上其他18
个域的setter方法的方案来设计这个类。但是你有没有想过这么几个问题?- 实例的创建先于可选域的添加之前,简而言之就是我必须得先实例化对象出来然后再调用setter方法给该实例添加可选域吧,而先实例化对象这种操作总是会隐含不必要的风险,所以这也是构造器的优点所在,构造器确保所有操作完成后再将一个完整的实例交付给使用者。但对于本问题,全部使用构造器来设置可选域更不现实。
- 使用setter方法就说明你并不打算把这个类设置成不可变类,那么这个类必然在并发问题上需要多思考一番(不明白?因为不可变类必定是线程安全的啦!!!当然你也可以选择在所有可选域设置完成之后,自我保证再也不动这个实例的状态,这样这个实例也是线程安全的,这叫事实不可变类,但显然你不能保证,人是容易犯错的)
二、解决问题的另一种方案--构建器
- 构建器的示例
interface ClassBuilder<T>{
T build();
}
public class BuilderTest {
//必选域
private final int field1;
private final int field2;
//可选域
private final int field3;
//省略...
private final int field20;
public static class Builder implements ClassBuilder<BuilderTest>{
//必选域
private final int field1;
private final int field2;
//可选域
private int field3;
//省略...
private int field20;
public Builder(int field1 , int field2) {
//必要的参数合法性检测代码...
this.field1 = field1;
this.field2 = field2;
}
public Builder setField3(int field3) {
//必要的参数合法性检测代码...
this.field3 = field3;
return this;
}
public Builder setField20(int field20) {
//必要的参数合法性检测代码...
this.field20 = field20;
return this;
}
@Override
public BuilderTest build() {
//必要的完整性检测代码...
return new BuilderTest(this);
}
}
private BuilderTest(Builder builder) {
this.field1 = builder.field1;
this.field2 = builder.field2;
this.field3 = builder.field3;
this.field20 = builder.field20;
}
public static void main(String[] args) {
BuilderTest test = new BuilderTest.Builder(20, 20).setField3(13).setField20(14).build();
}
}
构建器其实就是使用一个静态内部类专门负责类字段的填充这一工作,一般取名为Builder,每个Builder都会有build方法,故将其抽离出来变成了一个接口
三、分析与总结
- 由于BuilderTest类使用的是Builder对象作为参数,并从Builder对象中获取所需要的域的值,这样的写法简便且安全
- 可能唯一的缺点就是对小型的类(字段并不多)非常不友好,所以并不建议在字段少的类中使用构建器
参考文档:
[1] 《Effective Java》