Effective Java 读书笔记 (二)
创建和销毁对象
- 何时以及如何创建对象?
- 何时以及如何避免创建对象?
- 如何确保它们能够适时地销毁?
- 如何管理对象销毁之前必须进行的各种清理动作?
遇到多个构造器参数时要考虑使用构建器
静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。
那遇到这样的类,应该选择哪种方案来编写代码呢?
第一种方案:重叠构造器模式
就是以重载的方式来编写多个构造器。
缺点:当有许多参数的时候,客户端代码会很难编写,不便于使用。
第二种方案:JavaBeans模式
调用无参构造器创建对象,然后在调用setter方法来设置每个必要的参数。
缺点:在构造过程中JavaBean可能处于不一致的状态。JavaBeans模式使得把类做成不可变的可能性不复存在
第三种方案:建造者(Builder)模式
在类中构建一个builder静态成员,这个builder中拥有所有必要的参数构造器。在通过build()方法来构建出对象。例如:
//Builder Pattern
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
//Requred parameters
private final int servingSize;
private final int servings;
//Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize,int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val; return this;
}
public Builder fat(int val) {
fat= val; return this;
}
public Builder sodium(int val) {
sodium= val; return this;
}
public Builder carbohydrate(int val) {
carbohydrate= val; return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder){
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
注意NutritionFacts是不可变的,所有的默认参数值都单独放在一个地方。builder的设置方法返回builder本身,以便把调用链接起来,得到一个流式的API。下面就是其客户端代码:
NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).
calories(100).sodium(35).build();
Builder模式也使用于类层次结构。
假设用类层次根部的一个抽象类表示各式各样的匹萨:
//Builder pattern for class hierarchies
public abstract class Pizza{
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
//Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone() ;
}
}
public class NyPizza extends Pizza{
public enum Size { SAMLL, MEDIUM, LARGE}
private final Size size;
public static class Builder extends Pizza.Builder<Builder> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override
public NyPizza Build(){
return new NyPizza(this);
}
@Override
protected Builder self(){
return this;
}
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
public class Calzone extends Pizza {
private final boolean sauceInside;
public static class Builder extends Pizza.Builder<Builder> {
private boolean sauceInside = false; //Default
public Builder sauceInside(){
sauceInside = true;
return this;
}
@Override
public Calzone Build(){
return new Calzone(this)
}
@Override
public Builder self(){
return this;
}
}
private Calzone(Builder builder) {
super(builder);
sauceInside = builder.sauceInside;
}
}
这些“层次化构建器”的客户端代码本质上与简单的NutritionFacts构建器一样。为了简洁期间,下列客户端代码示例假设是在枚举常量上静态导入:
NyPizza pizza = new NyPizza.Builder(SMALL)
.addTopping(SAUSAGE).addTopping(ONION).build();
Calzone clazone = new Clazone.Builder()
.addTopping(HAM).sauceInside().build();
如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是一种不错的选择, 特别是当大多数参数都是可选或者类型相同的时候。