您的位置:首页 > 其它

第1条:考虑用静态工厂方法代替构造器

2014-06-19 21:21 459 查看
术语:

实例受控的类(instance-controlled):在某个时刻只能有某些规定的该类的实例存在。

基于接口的框架(interface-based framework):框架的对外访问与接入渠道是基于接口实现的。

服务提供者框架(Service Provider Framework):多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把它们从多个实现中解耦出来。

服务提供者框架的三个重要组件:

服务接口(Service Interface):这是提供者实现的。

提供者注册API(Provider Registeration API):这是系统用来注册实现的,让客户端访问它们的。

服务访问API(Service Access API):这是客户端用来获取服务的实例的。服务访问API一般允许但是不要求客户端指定某种选择提供者的条件。如果没有这样的规定,API就会返回默认实现的一个实例。这类API构成了框架的基础。

服务提供者框架的第四个可选组件:

服务提供者接口(Service Provider Interface):这是提供者负责创建其服务实现的实例,如果没有服务提供者接口,实现就按照类名称注册,并通过反射方式进行实例化。对于JDBC而言,Connection就是它的服务接口,DriverManager.registerDriver就是提供者注册API,DriverManager.get Connection就是服务访问API,Driver就是服务提供者接口。

类型推导(type inference):编译器根据上下文提供的参数类型来自动推导变量类型

与构造器相比,静态工厂方法的优势:

1、静态工厂方法与构造器不同的第一大优势在于,它们有名称。使用带有名称的静态工厂方法来获得类实例可以扩展代码的可读性,更能清晰的表达程序的意图。为了实现同一个类内存在多个签名的构造器,我们往往会选择提供多个具有不同签名的构造器,但是这种方法的可读性较差,不同的构造器之间的差别仅在于参数类型的顺序上。但是如果使用静态工厂方法,就可以通过名称来清楚的描述不同的构造器需要的参数。更具可读性。

2、静态工厂方法与构造器不同的第二大优势在于,不必在每次调用它们的时候都创建一个新的对象。这使得不可变类可以使用预先定义好的实例。避免了创建不必要的重复对象。书上给了一个例子:Boolean.valueOf(boolean)方法从来不创建对象。

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


如果程序经常性的请求创建相同的对象,那这种技术会大大的提升性能。还有就是静态工厂能够为重复调用返回相同的对象,这样就能方便我们来控制在某个时刻都应该有哪些特定的对象实例存在,这种类被称作实例受控的类。

实例受控的类的存在有几个原因:1、实例受控的类可以确保它是一个Singleton或者是不可实例化的。2、它还使得不可变的类可以确保不会存在两个相等的实例,即有当且仅当a==b的时候才有a.equal(b)为true。如果这得以保证,就可以使用==操作符来替代equal()方法。

3、静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型。这种技术有一个很重要的应用就是隐藏真正的实现类,而只提供公有的基类,以这种方式隐藏实现类会使得API变得非常简洁。书中指明,这项技术适用于基于接口的框架,因为在这种框架中,接口为静态工厂方法提供了自然返回类型。接口是不能持有静态方法的,因此按惯例接口Type的静态工厂方法被放在一个名为Types的不可实例化的类中。通过这种方法,用户可以知道被返回的对象是由相关的接口定义的,所以不需要阅读有关的类文档。使用静态工厂方法时,甚至要求客户端通过接口来引用被返回的对象而不是通过实现它们的类,这是一种良好的习惯。

静态工厂方法所返回的对象的类是可变的,这取决了传入给其的参数,其实就是一种约定的控制符。只要是已声名的实现类类型,都是可以被实例化返回的。这就大大的增加了灵活性。

静态工厂方法所返回的对象的类,在编写包含该静态工厂方法时甚至无需存在。比如服务提供者框架(如JDBC),下面是书中提供的一个例子

// Service provider framework sketch

// Service interface
public interface Service {
...// Service-specific methods go here
}

// Service provider interface
public interface Provider {
Service newService();
}

//Noninstantiable class for service registration and access
public class Services {
private Services() {} // Prevents instantiation

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


从上面的例子可以看出,服务的提供者负责提供Service接口的实现,而框架感知并支持服务是通过最终使用Provider接口,而这正是服务提供者向框架注册时使用的接口。

4、静态工厂方法与构造器不同的第四大优势在于,在创建参数化的类型实例的时候,它们使得代码变得更加简洁。在调用参数化类的构造器的时候,即使类型参数很明显也必须提供,比如说

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


与构造器相比,静态工厂方法的劣势:

1、静态工厂方法的主要缺点在于,类如果不含公有的或者受保护的构造器,就不能被子类化。这主是要由于JAVA的类继承机制的权限限制的。但是这样也会鼓励我们尽量使用类的复合而不是继承。

2、静态工厂方法的第二个缺点在于,它们与其他的静态方法实际上没有任何区别。这导致在API文档中,它们没有像构造器那样在API文档中明确的标注出来,因此对于提供了静态工厂方法而不是构造器的类来说,要想查明如何实例化一个类,这是非常困难的。可以采用惯用名的方式暂时弥补这一劣势。

valueOf :不太严格的讲,这个方法返回的实例与它的参数具有相同的值。实际这只是做了一个类型转换。

of:valuefOf的一种简洁的替代,在EnumSet中使用并流行起来。

getInstance:返回的实例是通过方法的参数来描述的,但是不能说与参数具有同样的值。参于Singleton来说,该方法就没有参数,并总是返回唯一的实现。

newInstance:像getInstance一样,但newInstance能够确保返回的每个实例都与所有其他的实例不同。

getType:像getInstance一样,但是在工厂方法处于不同的类型的时候使用。Type表示工厂方法所返回的对象类型。

newType:像getInstance一样,但是在工厂方法处于不同的类型的时候使用。Type表示工厂方法所返回的对象类型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: