定义

将一个复制对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,用户只需指定需要建造的类型就可以得到他们,建造过程及细节不需要知道。

使用场景

  • 如果一个对象有非常复杂的内部结构(很多属性)
  • 想把复杂对象的创建和使用分离

当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。

建造者与工厂模式区别

建造者模式:

  • 更注重于方法的调用顺序
  • 粒度:创建复杂的产品,由各种复杂的部件组成 工厂模式:
  • 注重于创建产品
  • 粒度:创建的都是一种类型的

    UML

    构建者模式 - 图1

    角色结构

  • Product: 最终要生成的对象。

  • Builder: 构建者的抽象基类(有时会使用接口代替)。其定义了构建Product的抽象步骤,其实体类需要实现这些步骤。其会包含一个用来返回最终产品的方法Product getProduct()
  • ConcreteBuilder: Builder的实现类。
  • Director: 决定如何构建最终产品的算法. 其会包含一个负责组装的方法void Construct(Builder builder), 在这个方法中通过调用builder的方法,就可以设置builder,等设置完成后,就可以通过builder的 getProduct() 方法获得最终的产品。

    优点

  • 建造者独立,易扩展。

  • 便于控制细节风险。 :::info 另外适用于快速失败,在 build 时可以做校验,如果不满足必要条件,则可以直接抛出创建异常,在 OkHttp3 中的 Request.Builder 中就是这样用的。
    public Request build() {
    if (url == null) throw new IllegalStateException(“url == null”);
    return new Request(this);
    } :::

    缺点

  • 产品必须有共同点,范围有限制。

  • 如内部变化复杂,会有很多的建造类。

    代码示例

    Computer.java 目标Computer类

    1. @Setter
    2. @ToString
    3. public class Computer {
    4. /**
    5. * 必须
    6. */
    7. private String cpu;
    8. /**
    9. * 必须
    10. */
    11. private String ram;
    12. /**
    13. * 可选
    14. */
    15. private int usbCount;
    16. /**
    17. * 可选
    18. */
    19. private String keyboard;
    20. /**
    21. * 可选
    22. */
    23. private String display;
    24. public Computer(String cpu, String ram) {
    25. this.cpu = cpu;
    26. this.ram = ram;
    27. }
    28. }

    ComputerBuilder.java 抽象构建者类

    1. public abstract class ComputerBuilder {
    2. public abstract void setUsbCount();
    3. public abstract void setKeyboard();
    4. public abstract void setDisplay();
    5. public abstract Computer getComputer();
    6. }

    MacComputerBuilder.java 实体构建者类

    ```java public class MacComputerBuilder extends ComputerBuilder {

    private Computer computer;

    public MacComputerBuilder(String cpu, String ram) {

    1. computer = new Computer(cpu, ram);

    } @Override public void setUsbCount() {

    1. computer.setUsbCount(2);

    } @Override public void setKeyboard() {

    1. computer.setKeyboard("苹果键盘");

    } @Override public void setDisplay() {

    1. computer.setDisplay("苹果显示器");

    } @Override public Computer getComputer() {

    1. return computer;

    }

  1. <a name="hJf3S"></a>
  2. #### LenovoComputerBuilder.java 联想电脑构建者类
  3. ```java
  4. public class LenovoComputerBuilder extends ComputerBuilder {
  5. private Computer computer;
  6. public LenovoComputerBuilder(String cpu, String ram) {
  7. computer = new Computer(cpu, ram);
  8. }
  9. @Override
  10. public void setUsbCount() {
  11. computer.setUsbCount(4);
  12. }
  13. @Override
  14. public void setKeyboard() {
  15. computer.setKeyboard("联想键盘");
  16. }
  17. @Override
  18. public void setDisplay() {
  19. computer.setDisplay("联想显示器");
  20. }
  21. @Override
  22. public Computer getComputer() {
  23. return computer;
  24. }
  25. }

ComputerDirector.java 指导者类(Director)

  1. public class ComputerDirector {
  2. public void makeComputer(ComputerBuilder builder){
  3. builder.setUsbCount();
  4. builder.setDisplay();
  5. builder.setKeyboard();
  6. }
  7. }

Client.java

  1. public class Client {
  2. public static void main(String[] args) {
  3. //1
  4. ComputerDirector director=new ComputerDirector();
  5. //2
  6. ComputerBuilder builder=new MacComputerBuilder("I5处理器","三星125");
  7. //3
  8. director.makeComputer(builder);
  9. //4
  10. Computer macComputer=builder.getComputer();
  11. System.out.println("mac computer:"+macComputer.toString());
  12. ComputerBuilder lenovoBuilder=new LenovoComputerBuilder("I7处理器","海力士222");
  13. director.makeComputer(lenovoBuilder);
  14. Computer lenovoComputer=lenovoBuilder.getComputer();
  15. System.out.println("lenovo computer:"+lenovoComputer.toString());
  16. }
  17. }

输出

  1. mac computer:Computer(cpu=I5处理器, ram=三星125, usbCount=2, keyboard=苹果键盘, display=苹果显示器)
  2. lenovo computer:Computer(cpu=I7处理器, ram=海力士222, usbCount=4, keyboard=联想键盘, display=联想显示器)

构造者模式的变种

  1. 首先其省略了director 这个角色,将构建算法交给了client端,其次将builder 写到了要构建的产品类里面,最后采用了链式调用。

如何实现

  • 在Computer 中创建一个静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中。
  • 在Computer中创建一个private的构造函数,参数为Builder类型
  • 在Builder中创建一个public的构造函数,参数为Computer中必填的那些参数,cpu 和ram。
  • 在Builder中创建设置函数,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例
  • 在Builder中创建一个build()方法,在其中构建Computer的实例并返回

    代码示例

    Computer.java

    1. @ToString
    2. public class Computer {
    3. /**
    4. * 必须
    5. */
    6. private String cpu;
    7. /**
    8. * 必须
    9. */
    10. private String ram;
    11. /**
    12. * 可选
    13. */
    14. private int usbCount;
    15. /**
    16. * 可选
    17. */
    18. private String keyboard;
    19. /**
    20. * 可选
    21. */
    22. private String display;
    23. private Computer(Builder builder){
    24. this.cpu=builder.cpu;
    25. this.ram=builder.ram;
    26. this.usbCount=builder.usbCount;
    27. this.keyboard=builder.keyboard;
    28. this.display=builder.display;
    29. }
    30. public static class Builder{
    31. private String cpu;//必须
    32. private String ram;//必须
    33. private int usbCount;//可选
    34. private String keyboard;//可选
    35. private String display;//可选
    36. public Builder(String cup,String ram){
    37. this.cpu=cup;
    38. this.ram=ram;
    39. }
    40. public Builder setUsbCount(int usbCount) {
    41. this.usbCount = usbCount;
    42. return this;
    43. }
    44. public Builder setKeyboard(String keyboard) {
    45. this.keyboard = keyboard;
    46. return this;
    47. }
    48. public Builder setDisplay(String display) {
    49. this.display = display;
    50. return this;
    51. }
    52. public Computer build(){
    53. return new Computer(this);
    54. }
    55. }
    56. }

    Client.java

    1. public class Client {
    2. public static void main(String[] args) {
    3. Computer computer = new Computer.Builder("因特尔", "三星")
    4. .setDisplay("三星24寸")
    5. .setKeyboard("罗技")
    6. .setUsbCount(2)
    7. .build();
    8. System.out.println(computer.toString());
    9. }
    10. }

    输出

    1. Computer(cpu=因特尔, ram=三星, usbCount=2, keyboard=罗技, display=三星24寸)

    框架示例