静态工厂方法相对构造器的优势:
- 静态工厂方法有名称。一个类只有一个带有指定签名的构造器。由于静态工厂方法有名称,所以不受限制。当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并选择合适名称突出区别。
- 静态工厂方法不必在每次调用的时候都创建一个新对象。静态工厂方法能为重复的调用返回相同的对象,有助于类能控制在某个时刻哪些实例应该存在。[这种类称为实例控制的类]
- 静态工厂方法可以返回原返回类型的任何子类型的对象。
public interface Service {
//........
}
public interface Provider {
Service newService();
}
public class Services {
private Services(){}
private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<Jenny>";
public static void registerDefultProvider(Provider p){
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
public static void registerProvider(String name, Provider p){
providers.put(name, p);
}
public static Service newInstance(){
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name){
Provider p = providers.get(name);
if(p == null){
throw new IllegalArgumentException("no provider registered with name: "+name);
}
return p.newService();
}
}
- 静态工厂方法在创建参数化实例的时候使代码变得更简洁。但是,在调用参数化类的构造器时,即使类型参数很明显,也必须指明。这需要两次提供类型参数:
Map<String,List<String>> m = new HashMap<String,List<String>>();
//eg:HashMap提供了这个静态工厂方法
public static <k, v> HashMap<k, v> newInstance(){
return new HashMap<k, v>();
}
//就可以通过以下代码替换上面繁琐的声明
Map<String,List<String>> m = HashMap.newInstance();
静态工厂方法的缺点:
- 类如果不含有公有的或受保护的构造器,就不能被子类化。
- 与其他的静态方法没有区别。
遇到多个构造器参数时,要用构建器
eg.多个构造器
public class NutritrionFats {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
public NutritrionFats(int servingSize, int servings){
this(servingSize, servings, );
}
public NutritrionFats(int servingSize, int servings, int calories){
this(servingSize, servings, calories, );
}
public NutritrionFats(int servingSize, int servings, int calories, int fat){
this(servingSize, servings, calories, fat, );
}
public NutritrionFats(int servingSize, int servings, int calories, int fat, int sodium){
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
}
}
JavaBean 构建器
public class NutritrionFats {
private int servingSize = -;
private int servings = -;
private int calories = ;
private int fat = ;
private int sodium = ;
public NutritrionFats(){ }
public void setServingSize(int val){ servingSize = val; }
public void setServings(int val){ servings = val; }
public void setCalories(int val){ calories = val; }
public void setFat(int val){ fat = val; }
public void setSodium(int val){ sodium = val; }
}
这种方式虽然弥补了重叠构造器的不足,但是,JavaBean自身存在不足,因构造过程被分到几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法通过构造参数的有效性来保证一致性。
下面是Builder模式来完善以上:
public class NutritrionFats {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
public static class Builder{
private final int servingSize;
private final int servings;
private int calories = ;
private int fat = ;
private int sodium = ;
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 NutritrionFats build(){
return new NutritrionFats(this);
}
}
private NutritrionFats (Builder build){
servingSize = build.servingSize;
servings = build.servings;
calories = build.calories;
fat = build.fat;
sodium = build.sodium;
}
}
注意:NutritrionFats是不可变的,所有的默认参数都放在一个地方,builder的setter方法返回builder本身,以便把调用连接起来。以下是调用客户端的代码: