建造者模式是什么?

建造者模式(Builder Pattern)是一种创建型的设计模式,它的主旨是对于构造过程比较复杂的对象,将这一过程从对象分离出来,由另一个对象(建造者)来对其进行构造。
建造者模式最早由 GoF 提出,它的关注点在于抽象,并且对复杂对象的构造处理非常到位,但原本的设计相对比较复杂。Joshua Bloch 在《Effective Java》一书中提出了一种改良版的建造者模式,该设计采用流式的编程风格,使得代码非常简洁易懂。

案例

用一个简单案例来帮助我们理解 Joshua Bloch 的建造者模式:假设现在有一个客户关系管理系统,我们定义了一个类 Customer 来表示客户资料。在创建客户资料时,需要录入客户的姓名、性别、出生年月等信息。

  1. public class Customer {
  2. private String firstName;
  3. private String lastName;
  4. private Gender gender;
  5. private Date dateOfBirth;
  6. private String phoneNumber;
  7. private String email;
  8. private Address address;
  9. public Customer(String firstName, String lastName, Gender gender, Date dateOfBirth, String phoneNumber,
  10. String email, Address address) {
  11. this.firstName = firstName;
  12. this.lastName = lastName;
  13. this.gender = gender;
  14. this.dateOfBirth = dateOfBirth;
  15. this.phoneNumber = phoneNumber;
  16. this.email = email;
  17. this.address = address;
  18. }
  19. // getters and setters
  20. }

我们在采集客户资料时,除了客户的姓名是必填的,其他的信息都是选填的。在大部分情况下,我们无法采集到客户的完整信息。如果我们采用全参构造器来创建客户对象,我们仍然不得不为缺失的信息传递值。如果客户资料包含的信息非常之多,达数十项,那么,这将会是一个灾难!

  1. new Customer("Mark", "Huey", Gender.MALE, null, null, "mark-huey@gmail.com", null);

如果我们采用 Joshua Bloch 的设计,那么这个问题将迎刃而解:

public class Customer {
    private String firstName;
    private String lastName;
    private Gender gender;
    private Date dateOfBirth;
    private String phoneNumber;
    private String email;
    private Address address;

    // getters and setters

    public static class Builder {
        private String firstName;
        private String lastName;
        private Gender gender;
        private Date dateOfBirth;
        private String phoneNumber;
        private String email;
        private Address address;

        public Builder(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

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

        // dateOfBirth method

        // phoneNumber method

        // email method

        // address method

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

    private Customer(Builder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.gender = builder.gender;
        this.dateOfBirth = builder.dateOfBirth;
        this.phoneNumber = builder.phoneNumber;
        this.email = builder.email;
        this.address = builder.address;
    }
}

现在,我们便可采取一种极为优雅的方式来创建客户资料:

Customer customer = new Customer.Builder("Mark", "Huey")
                .gender(Gender.MALE)
                .email("mark-huey@gmail.com")
                .build();

案例源码

可在 GitHub 上查看上述案例的完整代码。

优点与不足

Joshua Bloch 的建造者模式的设计有如下优点:

  • 可以灵活地创建对象,避免繁琐的全参构造方法,或者各种选参构造方法的排列组合。

但也略有不足:

  • 引入静态内部类 Build,增加了类的复杂度。如工程有依赖 Lombok,那么可以使用它的 @Builder 注解,可便捷地实现建造者模式。

参考资料

以下是本文参考的资料: