《Effective_Java》 Item1:Consider static actor methods instead of constructors

使用静态工厂的优势

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

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();
}
}

4.静态工厂可以简化繁琐的泛型对象的创建
        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。但工厂方法往往包含在其他类中。

小结:静态工厂和构造函数各有利弊,权衡后选择其一。不要急于使用构造函数,先考虑一下静态工厂。




阅读更多
个人分类: 笔记
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭