设计模式-Builder模式详解
Builder模式详解
Builder模式也叫做建造者模式,是设计模式的一种,
就是将复杂对象的创建变得简单明了,使对象与他的表示进行分离,使得同样的创建过程,可以创建不同的对象.
我们这里讲变种 Builder模式(更加简单,明了),并非真正意义上的Builder模式;这种模式的目的用于简化(不可变)对象的构造(比如 Google 的 Protobuf 协议,在生成为 Java 代码后,都会提供一个 Builder 类去构造相关 Message)
Builder模式作用
解决让Object始终保持valid状态的问题,解决具有大量参数的构造函数不好用的问题
先看普通构建User类:
public class User {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String toString() {
return "User(name=" + this.getName() + ", password=" + this.getPassword() + ")";
}
}
Builder模式:
public class UserB {
private final String name;
private final String password;
public UserB(UserB.UserBuilder builder) {
this.name = builder.name;
this.password = builder.password;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public static final class UserBuilder {
private final String name;
private String password;
private UserBuilder(String names) {
this.name = names;
}
public String toString() {
return "User(name=" + this.getName() + ", password=" + this.getPassword() + ")";
}
//构建参数是必传参数
public static UserB.UserBuilder create(String name) {
return new UserB.UserBuilder(name);
}
public UserB.UserBuilder withPassword(String password) {
this.password = password;
return this;
}
public UserB build() {
return new UserB(this);
}
}
}
这种传统的方式创建大家用的比较多,但是有很多缺点,如我要同时创建多个User,然后对应的User部分属性不一样,这样我要一个一个new就很麻烦,但是使用Builder模式就简单一点:
对应的main测试:
public static void main(String[] args) {
User user = new User();
user.setName("aaa");
user.setPassword("123");
System.out.println(user.toString());
UserB.UserBuilder aaa = UserB.UserBuilder.create("aaa");
aaa.withPassword("123");
UserB build = aaa.build();
aaa.withPassword("456");
UserB build1 = aaa.build();
aaa.withPassword("111");
UserB build2 = aaa.build();
System.out.println(build);
System.out.println(build1);
System.out.println(build2);
}
生成的Build1,build2是不同的…
通过new生成的对象,在进行set属性的时候,对象的构造过程是非连续的,也就是说对象可处于一个构造不完全的状态,我们很容易写出将对象传入各个方法,每个方法去赋值对象的某一部分这样的代码,这其实引入了一个状态空间,如果状态空间是强可控的,那还好(但依然提高了维护成本,你需要牢牢掌握住对象的构造过程,什么字段在何处被赋值);如果不可控,那么就很难保证这个对象是否被正确的构造,可能在某个方法中覆盖了某字段,也可能遗漏了某字段导致 NPE。java编程现在都往不可变对象走,就像String类.
Builder模式创建
一般都是在User类里面新建静态内部UserBuilder 类并且提供静态create方法返回Builder,然后就是属性方法,返回Builder,最后就是Build方法,返回对应的实体类.
对应的create方法一般是必传字段,这时候就可以设置为final的;而实体类里面属性就都是final的;
这种Builder模式几乎都差不多,如果实体类很多的话,我们会写很多重复的代码,这时候就很麻烦,推荐使用Lombok的 @Builder 注解,我们就不需要写任何代码了,我们看看通过@Builder注解生成的Builder类:
使用:
@Builder
@Data
public class UserLombok {
private final String name;
private final String password;
public static void main(String[] args) {
UserLombok.UserLombokBuilder build = new UserLombokBuilder().name("aa").password("aa");
UserLombok build1 = build.build();
UserLombok build2 = build.build();
System.out.println(build.name);
}
}
这里是对应的字节码,反编译的class类:
public class UserLombok {
private final String name;
private final String password;
public static void main(String[] args) {
UserLombok.UserLombokBuilder build = (new UserLombok.UserLombokBuilder()).name("aa").password("aa");
UserLombok build1 = build.build();
UserLombok build2 = build.build();
System.out.println(build.name);
}
UserLombok(String name, String password) {
this.name = name;
this.password = password;
}
public static UserLombok.UserLombokBuilder builder() {
return new UserLombok.UserLombokBuilder();
}
public String getName() {
return this.name;
}
public String getPassword() {
return this.password;
}
public String toString() {
return "UserLombok(name=" + this.getName() + ", password=" + this.getPassword() + ")";
}
public static class UserLombokBuilder {
private String name;
private String password;
UserLombokBuilder() {
}
public UserLombok.UserLombokBuilder name(String name) {
this.name = name;
return this;
}
public UserLombok.UserLombokBuilder password(String password) {
this.password = password;
return this;
}
public UserLombok build() {
return new UserLombok(this.name, this.password);
}
public String toString() {
return "UserLombok.UserLombokBuilder(name=" + this.name + ", password=" + this.password + ")";
}
}
}
同样,如果公司不推荐使用lombok的话,也可以使用idea 的Builder插件来生成部分代码,具体还得看公司的情况,统一才是王道…