标签:
原文链接:http://www.javacodegeeks.com/2013/01/the-builder-pattern-in-practice.html
我不会详细介绍这个模式,因为已经有大量的文章或者书籍对该模式进行过详细的解析。我将告诉你的是为什么以及什么时候你应该考虑使用它。值得一提的是,我所介绍的这个模式和设计模式四人帮的书(《设计模式:可复用面向对象软件的基础》)里面的有些许区别。四人帮书里面介绍的生成器模式重点在抽象出对象创建的步骤,并通过调用不同的具体实现从而得到不同的结果,本文介绍的生成器模式目的在于移除因多个重载构造函数,可选的参数和setters的过度使用引入的不必要的复杂性。
如果你的代码中定义了类似下面的拥有大量属性的User类,假设你想把该类定义为不可变的(顺便说一句,除非有足够的理由,否者你应该尽量将变量定义为不可变,我们会在另一篇文章中谈及。)
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
...
} 最简单有效的方法是定义一个只接收必选参数的构造函数,一个接收所有必选参数和一个可选参数的构造函数,一个接收所有必选参数和两个可选参数的构造函数,依次类推。这样的代码看起来是怎样的呢?大概如下所示:
public User(String firstName, String lastName) {
this(firstName, lastName, 0);
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName, age, '');
}
public User(String firstName, String lastName, int age, String phone) {
this(firstName, lastName, age, phone, '');
}
public User(String firstName, String lastName, int age, String phone, String address) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.phone = phone;
this.address = address;
} 那么对以上这些情况我们有其他的选择吗?我们可以遵循JavaBeans规范,定义一个默认无参构造函数,并对每个属性提供setters和getters函数,如下面所示:
public class User {
private String firstName; // required
private String lastName; // required
private int age; // optional
private String phone; // optional
private String address; //optional
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
幸运的是,我们有第三种选择:生成器模式。这种解决方案类似下面代码所示:
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;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
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 User getUser() {
return new
User.UserBuilder('Jhon', 'Doe')
.age(30)
.phone('1234567')
.address('Fake address 1234')
.build();
} 相当简洁,不是吗?你可以使用一行代码就创建User实例,更重要的是,代码易于阅读。而且你可以保证任何时候获取的User实例都处于完整的状态。这个模式相当灵活,一个builder可以在调用build函数之前通过设置不同的属性值来创建不同的类实例。builder甚至可以在每次调用之间自动补全生成的字段,例如id值或者序列号等。重要的一点是,类似构造函数,builder能够使得其参数为不可变的。build函数能够检查这些不可变参数并在参数无效时抛出IllegalStateException异常。
参数是从builder类拷贝到外部类,并在外部类而不是builder类中进行有效性校验的,这一点至关重要。原因在于builder类不是线程安全的,如果我们在创建外部类对象之前检查参数有效性,那么在参数校验和参数被拷贝到外部类的时间段之间,这些参数的值可能被另一个线程所更改。这个时间段就是著名的“脆弱之时”(“window of vulnerability”,脆弱之时,尤指冷战时期,美国的陆基导弹很容易成为苏联首次攻击目标的论点)。在我们的User例子中,代码类似下面所示:
public User build() {
User user = new user(this);
if (user.getAge() < 120) {
throw new IllegalStateException(“Age out of range”); // thread-safe
}
return 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);
}
该模式最后一个好处是builder可以传递给另外一个函数,使得该函数可以为调用者创建一个或者多个对象实例,而不需要知道对象实例的具体创建细节。为了达到这个目的,我们通常会定义一个简单的接口如下所示:
public interface Builder<T extends User> {
T build();
}
在前面的User例子中,UserBuilder类需要改为实现Builder<User>接口,这样一来,build函数类似如下所示:
UserCollection buildUserCollection(Builder<? extends User> userBuilder){...}
实战生成器模式(Builder Pattern In Practice)
标签:
原文地址:http://blog.csdn.net/asce1885/article/details/43271523