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

时间:2021-10-09 23:15:46

建造器模式,是于创建带有大量参数的对象,并避免因参数数量多而产生的一些问题(如状态不一致-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