当前位置:编程学习 > JAVA >>

Effective Java:Ch2_创建销毁对象:Item1_考虑用工厂方法替代构造函数

    本章的主题是创建和销毁对象:何时创建、怎样创建;何时应该避免创建、如何避免创建;如何确保对象适时被销毁;如何管理对象销毁前的清理动作。
 
        一个类如果要允许客户获得其实例,常用方法是提供一个public的构造函数。还有另外一个方法,也应该在每个程序员的工具集中占有一席之地:类可以提供一个public的静态工厂方法,这个方法返回类的实例。【例】下面是Boolean类(基本类型boolean对应的包装类)代码里的一个简单示例,该方法将一个boolean简单类型转换为一个Boolean对象引用。       
[java]  
public static Boolean valueOf(boolean b){  
    return b ? Boolean.TRUE : Boolean.FALSE;  
}  
        注意这里讲的静态工厂方法与设计模式中的工厂方法模式是不一样的,它在设计模式中没有直接等价物。
        类可以提供给它的客户静态工厂方法,用来替代构造方法,或作为构造方法的补充。用静态工厂方法来替代public构造方法既有优点也有缺点。
 
        优点一:静态工厂方法不同于构造函数,他们有名称。如果构造函数的参数不能确切地描述正被返回对象,那么具有合适名称的静态工厂方法就更易用,对应的客户端代码页更加易于阅读。【例】例如,构造方法BigInteger(int, int, Random)返回一个可能为素数的BigInteger,而用一个名为BigInteger.probablePrime()的静态工厂方法就更好。(JDK1.4最终增加了这个方法。)
[java]  
public class BigInteger extends Number implements Comparable<BigInteger> {  
  
    public static BigInteger probablePrime(int bitLength, Random rnd) {  
        if (bitLength < 2)  
            throw new ArithmeticException("bitLength < 2");  
  
        // The cutoff of 95 was chosen empirically for best performance  
        return (bitLength < SMALL_PRIME_THRESHOLD ?  
                smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :  
                largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));  
    }  
  
    public BigInteger(int bitLength, int certainty, Random rnd) {  
        BigInteger prime;  
  
        if (bitLength < 2)  
            throw new ArithmeticException("bitLength < 2");  
        // The cutoff of 95 was chosen empirically for best performance  
        prime = (bitLength < 95 ? smallPrime(bitLength, certainty, rnd)  
                                : largePrime(bitLength, certainty, rnd));  
        signum = 1;  
        mag = prime.mag;  
    }  
  
}  
        对于指定的方法签名,一个类只能有一个对应的构造函数。程序员通常这样来避开这个限制:可以提供两个构造函数,他们的参数列表仅仅是参数类型的顺序不同。这实在是一个坏主意。API的用户将不会记得这些构造函数哪个是哪个,常会无意地调用错误的构造函数。其他人读到使用这些构造函数的代码时,将不会知道这些代码是做什么的,除非参考类文档。
        由于静态工厂方法有名称,所以没有上述限制。假如一个类需要方法签名相同的多个构造函数,就用静态工厂方法来替代构造函数,并仔细地选择方法名称以便突出它们之间的区别。
 
        优点二:静态工厂方法不同于构造函数,无需每次被调用时都创建一个新对象。这使得非可变类(Item15)可以使用预先创建的实例,或者在创建实例时缓存起来,之后分发给客户,从而避免创建不必要的重复对象。Boolean.valueOf(boolean)方法演示了这种技术:它从不创建对象。这种技术类似于Flyweight模式。如果客户端经常请求创建相同的对象,那这种技术能极大的提高性能,尤其是当创建对象开销很大时。
        静态工厂方法可以再反复调用时返回同一个对象,这使得类可以严格控制在哪个时刻有哪些实例存在。这种类又被称为实例受控的类(instance-controlled)。编写实例受控的类有这么几个理由:第一,实例受控允许一个类确保是单例的(Item3),或者是不可实例化的(Item4);同时,实例受控允许非可变类(Item15)确保不会存在两个相等的实例,即当且仅当a==b时才有a.equals(b),如果一个类做出了这种保证,则它的客户端可以用==操作符来地带equals(Object)方法,这可能会提高性能。枚举类型(Item30)就保证了这一点。
 
        优点三:静态工厂方法不同于构造函数,它能返回任意子类型的对象。这让你在选择返回对象的类型时有了很大的灵活性。
        这种灵活性的一个应用是,API可以返回一个对象,而无需使对象的类public。用这种方式隐藏实现类能够产生一个非常紧凑的API。这种技术适用于基于接口的框架(interface based frameworks, Item18),在这种框架中,接口成为静态工厂方法的自然返回类型。由于接口不能有静态方法,所以按照惯例,接口Type的静态工厂方法被放在一个名为Types的不可实例化类(noninstantiable class, Item4)中。
        【例】例如,Java集合框架中有32个集合接口的便利实现,提供不可修改的集合、同步集合等等。几乎所有的实现都通过一个不可实例化类(java.util.Collections)中的静态工厂方法导出,返回对象的类都是非public的。——Collections.unmodifiableMap(Map)方法的返回类型是UnmodifiableMap,是个private类!
[java]  
public class Collections {  
    public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {  
        return new UnmodifiableMap<K,V>(m);  
    }  
  
    private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {  
    }  
}  
        如果把上述便利实现都作为public类导出,那集合框架API会臃肿很多。而且这并不仅仅是API数量的减少,还有conceptual weight的减轻。用户知道返回的对象都是由API通过接口精确定义了的,所以无需为实现类阅读额外的类文档。此外,使用静态工厂方法后,要求客户端通过接口来引用返回对象,而不是通过实现类来引用返回对象,这通常是一个好习惯。
 
        public静态工厂方法返回的对象类型不仅是可以非public,而且还能根据参数的不同,而返回不同的类型。只要是所声明的返回类型的任何子类型,都是允许的。为了提高软件可维护性和性能,返回类型还可以随着版本的不同而不同。
        【例】JDK1.5版本引入的java.util.EnumSet类没有public构造函数,只有静态工厂方法。根据底层枚举类型的大小,这些工厂方法可以返回两种实现:如果拥有64个或更少的元素(大多数枚举类型都是这样),静态工厂方法返回一个RegularEnumSet实例,用单个long来支持;如果枚举类型拥有65个或更多的元素,静态工厂方法则返回JumboEnumSet实例,用long数组来支持。
        这两种实现类的存在对客户端是不可见的。如果RegularEnumSet对于小的枚举类型提供性能优势,那么就能在以后的版本中删掉,并不产生副作用。同样,如果可以提高性能,以后版本可以增加EnumSet的第三或第四个实现。客户端不知道也不关心静态工厂返回的对象的类型,客户端只关心它是EnumSet的某个子类。       
[java]  
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>  
    implements Cloneable, java.io.Serializable  
{  
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {  
  &n
补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,