设计模式三十六计之建造者模式(Builder)

1. 设计意图

将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示。
建造者模式类图

2. 演示案例

假设我们需要创建一个用户对象,用户对象的属性有身份证号码、姓名、年龄、性别、族别、地址。最简单的方式是定义一个包含这六个属性的构造函数来完成对象的创建,但是,你想在构建的过程中只想包含其中的一个或者几个属性的时候,问题来了,没有与之对应的构造函数存在,也不可能提前定义多构造函数来覆盖这种动态传参的构造需求。这种类型的需求场景就需要建造者模式了(Builder)。

简而言之

建造者模式的要领是允许你创建不同风格的对象,同时避免构造函数被污染。当一个对象可能有N中风格存在或者对象的创建要涉及到多个步骤的时候适用于建造者模式

维基百科

构建器模式是一种对象创建软件设计模式,旨在寻找伸缩构造器反模式的解决方案。

在开始给出具体的代码前,我们先对比一下常规的一种设计方式。就上述的案例场景,我们可能会给出如下的一个构造函数:

  public User(String id,String name,int age,int gender,String nation,String address){
    //setter...
  }

在这种设计方式下,当用户属性改变时,构造函数参数的数量可能会很快失去控制,并且很难理解构造函数的参数排列,另外,如果用户属性继续增加,构造函数的参数列表将持续增长。这被称为伸缩构造反模式。

3. 程序示例

针对上述提到的问题,最合适的解决方案时使用建造者模式,首先我们需要创建一个User类:


public class User {

  private String id;    //身份证号码
  private String name;  //姓名
  private int age = 0;  //年龄
  private int gender = 0;//0:保密,1:男性,2:女性
  private String nation;  //族别
  private String address; //地址

  private User(Builder builder){
    this.id = builder.id;
    this.name = builder.name;
    this.age = builder.age;
    this.gender = builder.gender;
    this.nation = builder.nation;
    this.address = builder.address;
  }

  //getter...
  //setter...
  //toString...
}

然后我们需要创建一个Bulder类:

public static class Builder{

  private String id;    //身份证号码
  private String name;  //姓名
  private int age = 0;  //年龄
  private int gender = 0;//0:保密,1:男性,2:女性
  private String nation = "none";  //族别
  private String address = "none"; //地址

  /**
   * 假定建造时必须提供身份证号码和姓名
  **/
  public Builder(String id,String name){
    if(id == null || name == null){
      throw new IllegalArgumentException("id and name can not be null");
    }
    this.id = id;
    this.name = name;
  }

  public Builder withAge(int age){
    this.age = age;
    return this;
  }

  public Builder withGender(int gender){
    this.gender = gender;
    return this;
  }

  public Builder withNation(String nation){
    this.nation = nation;
    return this;
  }

  public Builder withAddress(String address){
    this.address = address;
    return this;
  }

  public User build(){
    return new User(this);
  }


}

说明:这里我们使用了一个静态内部类 Builder 来实现一个建造器

最后我们可以这样来创建一个用户对象:


public class TestUserBuilder{

  public static void main(String[] args){

    User zhangSan = new User.Builder("13579","张三").withAge(22).withGender(1).build();

    User wangWu = new User.Builder("24680","王五").withAge(30).withGender(1).withNation("汉族").build();

    User liLei = new User.Builder("123456","李蕾").withAge(18).withGender(2).withNation("苗族")
                         .withAddress("贵州省黔西南布依族苗族自治州").build();

    System.out.println(zhangSan.toString());
    System.out.println(wangWu.toString());
    System.out.println(liLei.toString());
  }
}

输入结果

id:13579,name:张三,age:22,gender:1,nation:none,address:none
id:24680,name:王五,age:30,gender:1,nation:汉族,address:none
id:123456,name:李蕾,age:18,gender:2,name:苗族,address:贵州省黔西南布依族苗族自治州

4.使用场景

满足一下需求的时候推荐使用建造者模式:

  1. 创建复杂对象的算法应该独立于组成对象的各个部分以及它们的组装方式。
  2. 构造过程必须允许对构造的对象进行不同的表示

5.建造者模式应用例子

6.总结

建造者模式使得对象内部的属性可以独立变化,使用者不必知道对象内部的组成细节,每个建造器相对独立,与其他的建造器无关。建造者模式的应用让对象创建过程更加灵活和易于控制。建造者模式也有相应的弊端,它使得对象的创建过程暴露给外界,让整个对象的 “加工工艺” 变得不透明。

参考