对象的创建

对于类而言,为了让客户端获取它自身的一个实例,最常用的方法就是提供一个公有的构造器。
还有一种方法,名为静态工厂方法(不对应于设计模式中的工厂方法).

我们应该考虑用静态工厂方法代替构造器。

一个静态工厂方法创建对象的简单例子:

1
2
3
4
5
6
7
public static Person newInstance(){
Person person = new Person();
if (person==null) {
throw new IllegalArgumentException("error");
}
return person;
}

优势:

  • 它们有名称
  • 不必在每次调用它们的时候创建一个新对象
  • 它们可以返回原返回类型的任何子类型的对象
  • 在创建参数化类型实例的时候,它们使代码变得更加简洁

缺点:

  • 类如果不含公有的或者受保护的构造器,就不能被子例化
  • 它们与其他的静态方法实际上没有任何区别

惯用名称:

  • valueOf —— 不太严格地讲,该方法返回的实例与它的参数具有相同的值。这样的静态工厂方法实际上是类型转换方法
  • of —— valueOf的一种更为简洁的替代。在EnumSet中使用并流行起来
  • getInstance —— 返回的实例是通过方法的参数来描述的,但是不能够说与参数具有相同的值。对于Singleton来说,该方法没有参数,并返回唯一实例
  • newInstance —— 像getInstance一样,但newInstance能够确保返回的每个实例都与其他实例不同
  • getType —— 像getInstance一样,但是在工厂方法处于不同类中的时候使用。Type表示工厂方法所返回的对象类型
  • newType —— 像newInstance一样,但是在工厂方法中处于不同的类中的时候使用。Type表示工厂方法中所返回的对象类型

遇到多个构造器参数时要考虑用构建器

静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。

对于多个参数的类,有以下较为常用的方法。

重叠构造器模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Person {
private int id;
private String name;
private String gender;
private String phone;
private String email;
private String address;
public Person(int id, String name) {
this(id, name, "", "", "", "");
}
public Person(int id, String name, String gender) {
this(id, name, gender, "", "", "");
}
public Person(int id, String name, String gender, String phone) {
this(id, name, gender, phone, "", "");
}
public Person(int id, String name, String gender, String phone,
String email) {
this(id, name, gender, phone, email, "");
}
public Person(int id, String name, String gender, String phone,
String email, String address) {
this.id = id;
this.name = name;
this.gender = gender;
this.phone = phone;
this.email = email;
this.address = address;
}
}

重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且难以阅读。

JavaBean模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Person {
private int id;
private String name;
private String gender;
private String phone;
private String email;
private String address;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setEmail(String email) {
this.email = email;
}
public void setAddress(String address) {
this.address = address;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getGender() {
return gender;
}
public String getPhone() {
return phone;
}
public String getEmail() {
return email;
}
public String getAddress() {
return address;
}
}

因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保持一致性。试图使用处于不一致状态的对象,将会导致失败,这种失败与包含错误的代码大相径庭,因此它调试起来十分困难。与此相关的另一点不足在于,JavaBean模式阻止了把类变成不可变的可能,这就需要程序员付出额外的努力来确保它的线程安全。

构建器模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class Person {
private final int id;
private final String name;
private final String gender;
private final String phone;
private final String email;
private final String address;
public Person(Builder builder) {
id = builder.id;
name = builder.name;
gender = builder.gender;
phone = builder.phone;
email = builder.email;
address = builder.address;
}
public static class Builder {
private int id = 0;
private String name = "";
private String gender = "";
private String phone = "";
private String email = "";
private String address = "";
public Builder id(int val) {
id = val;
return this;
}
public Builder name(String val) {
name = val;
return this;
}
public Builder gender(String val) {
gender = val;
return this;
}
public Builder phone(String val) {
phone = val;
return this;
}
public Builder email(String val) {
email = val;
return this;
}
public Builder address(String val) {
address = val;
return this;
}
public Person build() {
return new Person(this);
}
}
}

构建器模式不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或静态工厂),得到一个builder对象。然后客户端在builder上调用类似与setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法生成不可变的对象。

参考资料: 【Effective Java】中文版第2版