浅谈设计模式--建造器模式(Builder Pattern)

建造器模式,是于创建带有大量参数的对象,并避免因参数数量多而产生的一些问题(如状态不一致-JavaBean的setter模式)。

如果参数多且有些是必须初始化的,有些是不一定需要初始化的时候,创建对象是非常麻烦的,因为不得不为每种情况都添加一个构造方法。建造器模式,就是为了解决这个问题的。

使用Builder模式并不难:

1.创造一个静态内部建造类(Builder Class. e.g. UserBuilder)

2.类的构造方法必须设置为private,防止类被正常构造

3.建造类提供public方法,来设置可选的参数,并返回Builder对象

4.最后建造类提供build()方法,真正创建原来的类的对象

public class User {

    private final String firstName; // required
    private final String lastName; // required
    private final int age; // optional
    private final String phone; // optional
    private final String address; // optional

    private User(UserBuilder builder) {
    this.firstName = builder.firstName;
    this.lastName = builder.lastName;
    this.age = builder.age;
    this.phone = builder.phone;
    this.address = builder.address;
    }

    // a list of getter method

    @Override
    public String toString() {
    return firstName + " " + lastName + "-" + age + " , " + phone + "/"
        + address;
    }

    // Builder Class
    public static class UserBuilder {
    private final String firstName;
    private final String lastName;
    private int age;
    private String phone;
    private String address;

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

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

    public UserBuilder phone(String phone) {
        this.phone = phone;
        return this;
    }

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

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

    
    public static void main(String[] args) {
    // TODO Auto-generated method stub
    System.out.println(new User.UserBuilder("Jhon", "Doe").age(30)
        .phone("1234567").address("Fake address 1234").build()
        .toString());
    }
}

另外,在build()方法里,也可以检验参数的正确性,例如:

public User build() {
    User user = new user(this);
    if (user.getAge()<120) {
        throw new IllegalStateException(“Age out of range”); // thread-safe
    }
    return user;
}

这是线程安全的做法,因为user已经是不可变对象。下面的非线程安全做法,应该避免:

public User build() {
    if (age 120) {
        throw new IllegalStateException(“Age out of range”); // bad, not thread-safe
    }
    // This is the window of opportunity for a second thread to modify the value of age
    return new User(this);
}

但是,这种建造器的使用方法,其实是有隐患的:

1. 它没有指引用户,一步步的进行构建对象;用户并不知道何时何地用何方法

2. 状态不一致的风险仍然存在

如果需要构建顺序的话,可以做如下修改,来使建造器模式更加人性化:

public class NewUser {

    private final String firstName; // required
    private final String lastName; // required
    private final int age; // optional
    private final String phone; // optional
    private final String address; // optional

    private NewUser(UserBuilder builder) {
    this.firstName = builder.firstName;
    this.lastName = builder.lastName;
    this.age = builder.age;
    this.phone = builder.phone;
    this.address = builder.address;
    }

    // a list of getter method

    @Override
    public String toString() {
    return firstName + " " + lastName + "-" + age + " , " + phone + "/"
        + address;
    }

    public static interface FirstNameStep {
    LastNameStep firstName(String name);
    }

    public static interface LastNameStep {
    AgeStep lastName(String lastName);
    }

    public static interface AgeStep {
    PhoneStep age(int age);
    }

    public static interface PhoneStep {
    AddressStep phone(String phone);
    }

    public static interface AddressStep {
    BuildStep address(String address);
    }

    public static interface BuildStep {
    NewUser build();
    }

    // Builder Class
    public static class UserBuilder implements FirstNameStep, LastNameStep,
        AgeStep, PhoneStep, AddressStep, BuildStep {
    private String firstName;
    private String lastName;
    private int age;
    private String phone;
    private String address;

    private UserBuilder() {
    }

    public static FirstNameStep newBuilder() {
        return new UserBuilder();
    }

    public LastNameStep firstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public AgeStep lastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

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

    public AddressStep phone(String phone) {
        this.phone = phone;
        return this;
    }

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

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

    public static void main(String[] args) {
    NewUser user = NewUser.UserBuilder.newBuilder().firstName("ABC")
        .lastName("haha").age(10).phone("123").address("wa").build();
    System.out.println(user.toString());

    }

}

这个实现更加复杂,利用了接口的设计,使得建造器创建对象时,可以一步接着一步(firstName->lastName->age->phone->address),相当友好。缺点是,实现有点复杂,代码量比较大。

参考:

http://www.javacodegeeks.com/2013/01/the-builder-pattern-in-practice.html

http://rdafbn.blogspot.ie/2012/07/step-builder-pattern_28.html

原文地址:https://www.cnblogs.com/techyc/p/3538359.html