使用静态工厂的优势
1. 不同于构造函数,可以赋予静态工厂更有意义的名字
eg. BigInteger的构造方法BigInteger(int, int, Random)返回一个可能为质数的BigInteger,一个更好的名字是BigInteger.probablePrime(jdk在1.4中添加了此静态工厂)。
多个具有相同签名的方法容易令人混淆,可以通过使用不同名称的静态工厂避免之。
2. 静态工厂不同于构造函数,可以在调用时不创建新的对象。
eg.Boolean.valueOf(boolean b)。其实现接近于FlyWieght模式。valueOf返回共享的Boolean对象,节省开销。
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
静态工厂可以更好的控制对象的创建过程。它可以创建单件,阻止类实例化,保证不变对象的唯一性(a.equals(b) 当且仅当 a == b)。
3. 静态工厂不同于构造函数,可以返回子类对象。
隐藏具体实现的类型是这种灵活性的应用之一,可以有效地精简API。Interface-based frameworks就使用了此技术。由于接口不能定义提供static方法,用于创建类型Type的静态工厂按照惯例被放在一个以Types命名的不可实例化类中。例如java的collection框架除了基本的集合类之外,还提供了32个实用集合类的便捷实现,包括同步类(synchronized)和不可修改类(unmodified)等等。他们都通过不可实例化的Collections类中的静态工厂获得。
静态工厂不仅可以返回子类对象,而且可以根据参数在不同的调用中返回不同的对象。eg.java.util.EnumSet
无公有构造函数,只有静态工厂noneOf。如果EnumSet中的枚举类型的枚举值不超过64个,则返回RegularEnumSet(使用long实现),否则返回JumboEnumSet(使用long数组实现)。
这样,既可以避免用户记忆过多的API,又可以将功能的实现与客户端解耦,修改甚至替换隐藏类的实现对客户端没有任何影响。
Service provider frameworks同样利用了静态工厂的这个特点。在service provider frameworks中,被静态工厂返回的类甚至可以在静态工厂定义时不存在。例如JDBC。
Service provider frameworks包含三个关键模块。
- a service interface:描述服务,由服务提供者实现
- a provider registration API:用于注册服务提供者。服务提供者被注册后,才可以被客户访问
- a service access API:客户获取服务的方法
可选模块a service provider interface:约定提供者创建服务的方式
在JDBC中,Connection是一种服务,DriverManager.registerDriver是registration API,DriverManager.getConnection是access API。一个简单的service provider frameworks
4.静态工厂可以简化繁琐的泛型对象的创建
public interface Service {
// service here
}
public interface Provider {
Service getService();
}
// Noninstantiable class for service registration and access
public class Services {
private ConcurrentHashMap<String, Provider> providers = new
ConcurrentHashMap<String, Provider>();
public static final DEFAULT_SERVICE_NAME = “<default>”;
public registerProvider(Provider p) {
registerProvider( DEFAULT_SERVICE_NAME, p);
}
public registerProvider(String name, Provider p) {
providers.put(name, p);
}
// Service access API
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();
}
}
eg. 我们现在必须写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(); 我们可以在自己的util类和自定义的泛型类中加入该静态工厂。
静态工厂的缺点
1. 只有静态工厂的类无法被继承。因为继承需要类中包含public或protected方法。
2. 静态工厂不能很好的同其它静态方法区分开。API文档中不能突出它们的特殊用途。用户可能不知道如何实例化这个类。
常用的静态工厂方法名
- valueOf:常用于类型转换,如Boolean.valueOf
- of:valueOf的简化版,如EnumSet.of
- getInstance, newInstance: 前者可能不创建新的对象。
- getType, newType : 同getInstance和newInstance。但工厂方法往往包含在其他类中。
小结:静态工厂和构造函数各有利弊,权衡后选择其一。不要急于使用构造函数,先考虑一下静态工厂。