今天写一个构建者模式,通常我们使用javaBean都是定义私有属性,生成getset方法。这种方式很常见,在属性较少的时候,可以很好地使用,但是一旦属性爆炸时,那么意味着我们会有无穷无尽的set代码要写。比如我们现在一个主表有50个字段左右,还有请求别人接口时,dto也有50多个属性字段要写。那么每次持久化之前或者发送之前的报文组装,都要set属性值。有时候写着写着自己就乱了。有可能存错值。或者在你成功组装好dto或者javabean之后,后续的代码无意中set了一个错值。那么这个值就会被持久化或者发送出去。那么这次请求就算是出错了。所以为了避免这样的情况发生。也避免自己写set写吐了。我们可以使用建造者模式创建一个对象。
好了话不多说,直接上代码。
这是我手写的一个建造者javabean
public class TestUser {
private final int id;
private final String name;
private final int age;
private final List<String> items;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public List<String> getItems() {
return items;
}
@Override
public String toString() {
return "TestUser{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", items=" + items +
'}';
}
private TestUser(int id, String name, int age, List<String> items) {
this.id = id;
this.name = name;
this.age = age;
this.items = items;
}
public static testUserBuilder builder(){
return new testUserBuilder();
}
public static class testUserBuilder{
private int id;
private String name;
private int age;
private List<String> items;
public testUserBuilder id(int id){
this.id = id;
return this;
}
public testUserBuilder name(String name){
this.name = name;
return this;
}
public testUserBuilder age(int age) {
this.age = age;
return this;
}
public testUserBuilder items(List<String> items) {
this.items = items;
return this;
}
public TestUser build(){
return new TestUser(id, name, age, items);
}
}
}
先定义了4个私有final属性,然后只给他们提供get方法。不允许他们被set值,这就可以避免对象被创建之后值再次被修改的问题。
然后我们给他提供一个全参私有构造器,全参是为了给final属性初始化时赋值。私有是避免了该类被直接实例化。
我们只允许该类使用其内部类进行实例化操作。所以我们再给他提供一个builder方法,新建一个内部类testUserBuilder。
testUserBuilder类含有和testUser相同类型的属性。并且不能是final,使其在初始化之后允许我们再次给属性赋值。
然后我们提供对应属性的复制方法,例如:
public testUserBuilder id(int id){
this.id = id;
return this;
}
我们将传入的参数值赋值内部类的属性。最后我们使用build方法,返回TestUser的一个实例。
然后我们测试一下:
TestUser wm = TestUser.builder().id(1).name("wm").age(10).items(list).build();
System.out.println(wm.toString());
System.out.println(wm.getId());
System.out.println(wm.getName());
System.out.println(wm.getAge());
System.out.println(wm.getItems());
先调用TestUser的静态方法builder,生成一个TestUserBuilder对象实例,然后调用赋值方法赋值。最后调用build方法返回TestUser对象实例。
测试结果:
TestUser{id=1, name='wm', age=10, items=[aa, bb]}
1
wm
10
[aa, bb]
其实还有一个更简便的犯法实现建造者模式,就是使用插件lombok。
我们刚才上面手写的建造者模式几个重点是:1,构建器;2,私有全参构造器;3,get方法
那么我们可以使用lombok提供的注解实现。例如:
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString(exclude = {"age"})
@Getter
public class TestUser {
private final int id;
private final String name;
private final int age;
private final List<String> items;
}
我们代码简洁了很多,只需要写我们需要的属性即可。编译过后,我们看一下class文件:
public class TestUser {
private final int id;
private final String name;
private final int age;
private final List<String> items;
public static TestUser.TestUserBuilder builder() {
return new TestUser.TestUserBuilder();
}
private TestUser(final int id, final String name, final int age, final List<String> items) {
this.id = id;
this.name = name;
this.age = age;
this.items = items;
}
public String toString() {
return "TestUser(id=" + this.getId() + ", name=" + this.getName() + ", items=" + this.getItems() + ")";
}
public int getId() {
return this.id;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public List<String> getItems() {
return this.items;
}
public static class TestUserBuilder {
private int id;
private String name;
private int age;
private List<String> items;
TestUserBuilder() {
}
public TestUser.TestUserBuilder id(final int id) {
this.id = id;
return this;
}
public TestUser.TestUserBuilder name(final String name) {
this.name = name;
return this;
}
public TestUser.TestUserBuilder age(final int age) {
this.age = age;
return this;
}
public TestUser.TestUserBuilder items(final List<String> items) {
this.items = items;
return this;
}
public TestUser build() {
return new TestUser(this.id, this.name, this.age, this.items);
}
public String toString() {
return "TestUser.TestUserBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", items=" + this.items + ")";
}
}
}
可以看出来,跟我们写的没什么太大差别。
这是一种不变模式结合了建造者模式的方法,尤其适合组装之后即最后的结果无需改变并且属性爆炸;比如长报文或者持久化时javabean的最后组装。
其实还可以根据自己的需求进行改变。比如你的值需要经常set改变,那么其实可以放开final并提供set方法。这就要根据需求来看了。