第一条:考虑用静态工厂方法代替构造器
首先要指明一个误区:静态工厂方法不是指的设计模式里面的工厂方法,他是指以静态方法的形式创建对象(工厂就是用来创建对象的),例如:
public static User createNormalUser(){
return new User();
}
public static User createVIPUser(){
return new User(100);
}
书中描述这样做的优点有以下几点:
1.他们有名称。通过以上例子就能看出来,有无名称的好处,创建普通用户和创建VIP用户的区别仅仅在于创建是是否传入参数,显然静态工厂方法更容易使用,产生的客户端代码也更易阅读。
2.不必在每次调用它们的时候创建一个新对象。这条指的是单例模式的用法。
3.它们可以返回类型的任何子类型的对象。这句话不太好理解,最起码我是这样。刚开始我以为是向上转型,可是转念一想,向上转型还需要静态工厂方法?然后仔细一看是返回的子类对象。像Collections里的方法:
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
不管是UnmodifiableRandomAccessList类还是UnmodifiableList类,它们都是在Collections里面的内部类。Collections里面包含大量的这样的内部类。书中也说明了这样的好处,可以返回对象,同时又不会使对象的类变为共有,这样使得实现类变得非常简洁。大家可以自己去查看Collections类去体会一下。
4.在创建参数化类型实例的时候,他们使得代码更简单。书中例子:
//以前版本
Map<String, List<String>> m = new HashMap<String, List<String>>();
//改为静态工厂后
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
//实例化
Map<String, List<String>> m = HashMap.newInstance();
其实这个优点对于现在的JDK版本来说也不叫优点了。
//1.7之后
Map<String, List<String>> m =new HashMap<>();
书中阐述的静态工厂方法缺点有两个。
1.类如果不含共有的或者受保护的构造器,就不能被实例化。例如Collections里面的UnmodifiableRandomAccessList和UnmodifiableList就不能被实例化。
2.与其他静态方法没有任何区别。所以无法区别哪些是静态工厂方法和其他静态方法。书中鼓励用命令规范来弥补这个缺点。以下是常用名称:
valueOf(),of(),getInstance(),newInstance(),getType(),newType()
第二条:遇到多个构造器参数时要考虑选用构建器
大家肯定都遇到过这种情况:
public Human(int height, int age) {
this.height= height;
this.age= age;
}
public Human(int age) {
this.age= age;
}
public Human() {}
public static void main(String[] args) {
Human h= new Human(170,17);
}
当创建一个对象时,对于某些可选的参数属性,大多数人都是用重载构造器来实现,也就是书中所述重叠构造器。这样做的缺点在于创建对象的时候,表达不清晰,容易出错。例如new Human(170,17),如果你把170和17填返了,那么程序还是会正常运行的。对于参数更多的对象,可能一点不小心就会造成一些微妙的错误,而且程序并不能给我们这些错误任何提示。
然后书中阐述了另外一种方式,javabean模式。即:
private int height;
private int age;
public void setHeight(int height) {
this.height = height;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Human h= new Human();
h.setHeight(170);
h.setAge(17);
}
这样的好处在于设置参数的时候有了名字,这样就大大减轻了出错的几率。阅读起来也很容易。可是这也同样有着严重的缺陷,因为构造被分到了几个调用中,所以可能会出现线程安全的问题。实例化类应该是一气呵成的。
因此书中阐述了第三个解决方案:构建器
public class Human {
private int height;
private int age;
private String body;
public Human(Builder builder) {
this.age = builder.age;
this.body = builder.body;
this.height = builder.height;
}
static class Builder{
private int height=0;
private int age;
private String body="";
public Builder(int age) {
this.age = age;
}
public Builder setHeight(int height) {
this.height = height;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setBody(String body) {
this.body = body;
return this;
}
public Human build(){
return new Human(this);
}
}
public static void main(String[] args) {
Human human = new Builder(17).setHeight(170).build();
}
}
这样优点在于既能保证重叠构造器的安全性,又能保证javaBean模式的可读性。完美融合了前面两种的优点。而且还能保证某些必要的参数,例如age属性。构建器的缺点在于创建对象的时候,必须先创建Builder构建器,增加了创建对象的额外开销。