如果你足够细心的话,可以发现越来越多的类库其对象初始化方式都不再是传统的构造器方式创建,而是通过 build
方式创建。那 build
方式构造对象和基础的构建器初始化对象又有什么区别?在代码中又该如何使用 build
构造器?又应当在什么应用场景下使用呢?
下面就逐一解答上述几个疑问,让我们进入正文吧。
一、示例介绍
下面以常用的网络请求类库 OkHttp
为例,其 Request
对象创建正是通过 build
方式实现。
如下示例中通过 build 方式创建了一个基本的 post
类型请求对象。
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
二、配置教程
1. 实现原理
build
方式构建的对象的原理十分简单,就是通过内部类实现数据赋值的一个过程。
新建 TestEntity
类用于后续初始化创建,其中 Builder
内部类正是实现 build
方式初始化关键。
TestEntity
类的完整代码如下:
public class TestEntity {
private String id;
private String name;
private String describer;
private TestEntity(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.describer = builder.describer;
}
public static Builder builder() {
return new Builder();
}
/**
* 创建内部静态类 Builder
*/
public static class Builder {
private String id;
private String name;
private String describer;
public Builder id(String val) {
this.id = val;
return this;
}
public Builder name(String val) {
this.name = val;
return this;
}
public Builder describer(String val) {
this.describer = val;
return this;
}
public TestEntity build() {
return new TestEntity(this);
}
}
}
2. 示例演示
完成上述 TestEntity
类的创建之后,下面就使用其并通过 build
方式创建对象。
从下述中创建的两个测试对象可以清晰的明白对象每个属性字段赋予的值,并且提供可以随意打乱赋值顺序而无需创建对应的构造器,也可以避免繁杂的 setter
方法。
@Test
public void demo() {
TestEntity property1 = TestEntity.builder()
.id("123456")
.describe("I am Alex")
.build();
System.out.println(property1.toString());
TestEntity property2 = TestEntity.builder()
.name("Beth")
.describe("I am Beth")
.build();
System.out.println(property2.toString());
}
三、继承场景
1. 父类定义
在上述的示例中是针对单个类而言,而在具体的系统设计中,通常存在类的继承关系,此时则需要对上述模式进行一定的微调。
首先以父类 BaseEntity
为例,和之前提到的定义方法一致,为了节省篇幅此处仅定义当个属性。
public class BaseEntity {
private Integer id;
public BaseEntity(Builder builder) {
this.id = builder.id;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private Integer id;
public Builder id(Integer val) {
this.id = val;
return this;
}
public BaseEntity build() {
return new BaseEntity(this);
}
}
}
2. 子类定义
创建声明子类 UserEntity
继承于父类 BaseEntity
,同样在类中定义内部静态类 Builder
,注意其同样需要继承于父类的 Builder
。
此时有两种处理方法,若不重写父类中的属性构建方法则使用时必须优先定义子类的属性,或者将父类 Builder
中的属性定义为 protected
,并在子类重写对应的赋值方法。
这里选择第一种方式进行定义,代码如下:
package xyz.ibudai.practice.design.builder.entity;
public class PersonEntity extends BaseEntity {
private String city;
public PersonEntity(Builder builder) {
// 父类在对象调用时已创建,此时则进行赋值
super(builder);
this.city = builder.city;
}
public static Builder builder() {
return new Builder();
}
/**
* 调用静态类 Builder 时会初始对象,并初始化其父类
*/
public static class Builder extends BaseEntity.Builder {
private String city;
public Builder city(String val) {
this.city = val;
return this;
}
@Override
public PersonEntity build() {
return new PersonEntity(this);
}
}
}
3. 示例演示
在上一点提到选择不重写父类的 Builder
的内部方法,则下述初始化第一个属性必须为子类成员变量。
@Test
public void demo2() {
BaseEntity entity = PersonEntity.builder()
// 子类 city 必须第一个定义,后续则可无序
.city("NewYork")
.id(1)
.build();
System.out.println(entity);
}
4. 覆盖重写
这里同样分享一下另一种定义方式,在父类中需要将 Builder
成员作用域声明为 protected
。
public class BaseEntity {
private Integer id;
public BaseEntity(Builder builder) {
this.id = builder.id;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
/**
* 这里需要定义为 protected
*/
protected Integer id;
public Builder id(Integer val) {
this.id = val;
return this;
}
public BaseEntity build() {
return new BaseEntity(this);
}
}
}
同样创建子类 PersonEntity
,需要注意其要和父类在同一包路径下,
public class PersonEntity extends BaseEntity {
private String city;
public PersonEntity(Builder builder) {
super(builder);
this.city = builder.city;
}
public static Builder builder() {
return new Builder();
}
public static class Builder extends BaseEntity.Builder {
private String city;
/**
* 重写父类方法
*/
@Override
public BaseEntity.Builder id(Integer val) {
return super.id(val);
}
public Builder city(String val) {
this.city = val;
return this;
}
public Builder city(String val) {
this.city = val;
return this;
}
@Override
public PersonEntity build() {
return new PersonEntity(this);
}
}
}