Effective Java 2nd Edition Reading Notes
Item1: Consider use static factory methods instead of Constructors
考虑使用静态工厂方法来取代构造方法。
静态工厂方法实例:
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
使用静态工厂方法具有如下的优点:
1. 静态工厂方法可以有不同的名字。(对于构造方法来说,名字都是相同的,即类名),所以可以使用C.getInstanceByA(int,String)和C.getInstanceByB(int,String),但是不能定义参数类型和个数一样的构造方法。虽然也可以通过定义C(int,String)和C(String,int),但是这样定义给用户带来困扰。
所以,当一个类的多个构造方法具有相同的声明时,可以使用静态工厂方法来指定具有意义的名字。
2. 使用静态工厂方法不必每次都创建新的实例。构造方法是每次调用都会构造新的实例的。这样不变的类(immutable)就可以使用预先构造的实例了。例如上面的Boolean类。或者可以缓存已经构造的实例(放在Collection或者数组中),以避免创建重复的实例(如果相同的实例被频繁的调用的话,可以提高性能)。
类可以通过静态工厂方法来控制生成的实例,例如单例(singleton)。
3. 静态工厂方法可以返回声明的返回值类型的子类型。这样可以使得子类型不需要被声明为public的。减少类层次概念上的繁琐。用户只关注返回值类型即可。例如:
public static Dog getDog(int price){
if(price > 500){
return BigDog();
}else{
return SmallDog();
}
}
用户只关注买到的Dog,而无论是BigDog还是SmallDog,都具有Dog接口的bark()方法就可以了。如果有一天,商店不再销售SmallDog,那么移除SmallDog对结果没有影响。因为实现被隐藏了。
另外一种情况就是具体的实现可以不被提供,而只是定义好接口。JDBC就是一个典型的这种情况。具体的JDBC Driver并不包含在JDBC API中,而是由数据库厂商提供。JDBC使用了服务提供商框架(service provider framework),即所有的服务提供商对公开的规范接口进行实现,而规范隐藏具体实现的细节。该框架由三部分组成:
服务接口 - service interface
提供者注册API - provider registration API
服务访问API - service access API – 一般说来就是一个灵活的静态工厂方法,用户可以指定参数或者获取默认的服务实现
In the case of JDBC, Connection plays the part of the service interface, DriverManager.registerDriver is the provider registration API, DriverManager.getConnection is the service access API, and Driver is the service provider interface.
下面是一个简单的service provider framework:
http://javatips4u.googlecode.com/svn/trunk/
4. 静态工厂方法还可以降低使用泛型时的繁琐。例如在下面的代码中,必须指定参数类型:
Map<String,List<String>> map = new HashMap<String,List<String>>();
当类型参数过于复杂时,使用这种定义是比较痛苦的。如果使用静态工厂方法,就不需要明确的指定类型:
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
编译器可以自动的推断类型,这叫做type inference。此时就可以通过下面的语句来实例化一个Map对象:
Map<String, List<String>> m = HashMap.newInstance();
虽然目前的JDK中还不包含类似的工厂方法,但是开发人员可以将其添加到自己的Utility类中,或者在自定义的泛型类中定义这样的静态工厂方法。
静态工厂方法也包含如下的缺点:
1. 只包含静态工厂方法 (没有public或者protected构造方法) 的类无法被继承。但是由于设计模式中推荐使用合成而非继承,所以这一点存在争议。
2. 无法与其他的静态方法明确的区分。
常见的静态工厂方法名称如下:
• valueOf—返回与参数值相同的实例,主要是类型转换方法。例如String.valueOf(), Boolean.valueOf()。
• of—valueOf的简化版,EnumSet类使用。
• getInstance—返回由参数确定的实例。但是可能具有不同的值,在单例的模式下,getInstance不包含参数,并且返回唯一的实例。
• newInstance—和getInstance类似,只是该方法确保返回的实例与其他的实例不同。
• getType—和getInstance类似,但是用于工厂方法位于不同的累中时。Type表示工厂方法返回的对象的类型。
• newType—和newInstance类似,但是用于工厂方法位于不同的累中时。Type表示工厂方法返回的对象的类型。