设计模式-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插件来生成部分代码,具体还得看公司的情况,统一才是王道…

posted @ 2020-05-15 18:08  你就像甜甜的益达  阅读(285)  评论(0编辑  收藏  举报