Effective Java 读书笔记——1:考虑用静态工厂方法代替构造器

类可以提供一个公有的静态工厂方法(static factory method),是一个用来返回类的实例静态方法。比如,下面可以通过boolean来返回Boolean对象引用的方法:

	public static Boolean valueOf(boolean b) {
		return b ? Boolean.TRUE : Boolean.FALSE;
	}

上面是一个非常简单的静态工厂方法,我们进一步可以看到Boolean.TRUE也是非常直观简单的:

	public static Boolean valueOf(boolean b) {
		return b ? Boolean.TRUE : Boolean.FALSE;
	}...

注意,这里所说的静态工厂方法和设计模式中的工厂方法模式存在一点区别。下面进一步说明的是,类可以通过静态工厂方法来提供它的实现,而不是构造器。

优势

  1. 静态工厂方法有名称。尽管构造器可以通过传入参数的不同,进行重载。但是,往往有限的参数无法正确描述需要返回的对象,这时候,具有适当名称的静态工厂更容易使用,代码也更容易阅读。
  2. 不必每次调用的时候都创建一个新对象。这样可以使不可变类重复使用相同的实例,比如上面这个例子。
  3. 可以返回原返回类型任何子类型的对象。这种灵活性的一种应用是:API可以返回对象,同时又不会使这种对象的类变成公有的。以这种方式隐藏实现类会使API变的简洁。如,Java Collection集合类。
比如在java.util.EnumSet中就没有公有构造器,只有静态工厂方法。它返回两种实现类之一,根据底层枚举类型的大小,如:
    /**
     * Creates an empty enum set with the specified element type.
     *
     * @param <E> The class of the elements in the set
     * @param elementType the class object of the element type for this enum
     *     set
     * @return An empty enum set of the specified type.
     * @throws NullPointerException if <tt>elementType</tt> is null
     */
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");

        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }

然而这两种实现类,对于客户端来说是不可见的。未来的版本中,可能会删除没有用的实现类,或者是加上更多的实现类,这对API是没有任何影响的。

服务提供框架

静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类的时候可以不必存在。这种灵活的静态工厂方法构成了服务提供者框架(Service Provider Framework)的基础。如,JDBC API。服务提供框架是指这样一个系统: 多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把他们从多个实现中解耦出来。
服务提供者框架中,有三个重要组成组件:
  1. 服务接口(Service Interface)。提供者实现的。
  2. 提供者注册API(Provider Registeration API)。系统用来注册实现的,让客户端来访问。
  3. 服务访问API(Service Access API)。客户端用来获取服务实例。服务访问API一般允许但是不要求客户端指定某种选择提供者的条件,如果没有这样的规定,API就会范围默认的一个实现实例。服务访问API是“灵活的静态工厂”,它构成该框架的基础。
下面就是一个简单实现,包含一个服务提供者接口和默认提供者:
public interface Service {
    // Service-specific methods go here
}

public interface Provider {
    Service newService();
}

public class Services {
    private Services() { }  // Prevents instantiation (Item 4)

    // Maps service names to services
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";

    // Provider registration API
    public static void registerDefaultProvider(Provider p) {
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }
    public static void 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();
    }
}


缺点

  1. 类如果不含有公有的或者受保护的构造器,就不能被子类化。这是显而易见的,一般静态工厂方法返回对象,还是要通过一个构造器(常常是受保护)来进行,无论是自身所在的类,还是返回其他的类。
  2. 静态工厂方法和其他静态方法是一样的,很难明确标示出来。对于通过静态工厂方法来实例化一个类的类来说,想查明如何实例化这个类是比较困难的。因此,常常会通过一些惯用名称来弥补这一劣势,如:valueOf,of,getInstance,newInstance,getType,newType.
静态工厂方法和公有构造器同样重要,切忌第一反应是提供公有构造器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值