对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。还有一种方法,也应该在每个程序员的工具箱中占有一席之地。类可以提供一个公有的静态工厂方法( static factory method ),它只是一个返回类的实例的静态方法。
本条目中所指的静态工厂方法并不直接对应于设计模式( Design Pattern )中的工厂方法。如果不通过公有的构造器, 或者说除了公有的构造器之外,类还可以给它的客户端提供静态工厂方法。提供静态工厂方法而不是公有的构造器,这样做既有优势,也有劣势。
静态工厂的优势
1. 静态工厂方法与构造器不同的第一大优势在于它们有名称。
当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并且仔细地选择名称以便突出静态工厂方法之间的区别。如:
构造器Biginteger (int , int, Random )返回的Biginteger 可能为素数,如果用名为Biginteger.probablePrime 的静态工厂方法来表示,显然更为清楚(Java 4 版本中增加了这个方法。)
2.静态工厂方法与构造器不同的第二大优势在于不必在每次调用它们的时候都创建一个新对象。
这使得不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象。
3.静态工厂方法与构造器不同的第三大优势在于它们可以返回原返回类型的任何子类型的对象。
API 可以返回对象,同时又不会使对象的类变成公有的。以这种方式隐藏实现类会使API 变得非常简洁。
4.静态工厂的第四大优势在于,所返回的对象的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。
只要是已声明的返回类型的子类型,都是允许的。返回对象的类也可能随着发行版本的不同而不同。
5.静态工厂的第五大优势在于方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在。
这种灵活的静态工厂方法构成了服务提供者框架( Service Provider Framework)的基础,例如JDBC(Java 数据库连接)API 。
服务提供者框架是指这样一个系统:
多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把它们从多个实现中解稠出来。
服务提供者框架中有三个重要的组件:服务接口( Service Interface ),这是提供者实现的;提供者注册API ( Provider Registration API ),这是提供者用来注册实现的;服务访问API (Service Access API) ,这是客户端用来获取服务的实例。
服务提供者框架的第四个组件服务提供者接口( Service Provider Interface )是可选的,它表示产生服务接口之实例的工厂对象。如果没有服务提供者接口,实现就通过反射方式进行实例化。
静态工厂的缺点
1.静态工厂方法的主要缺点在于类如果不含公有的或者受保护的构造器,就不能被子类化。**
这样也许会因祸得福,因为它鼓励程序员使用复合( composition),而不是继承,这正是不可变类型所需要的。
2.静态工厂方法的第二个缺点在于,程序员很难发现它们。
在API 文档中,它们没有像构造器那样在API 文档中明确标识出来,因此对于提供了静态工厂方法而不是构造器的
类来说,要想查明如何实例化一个类是非常困难的。
下面是静态工厂方法的一些惯用名称:
- from一一类型转换方法,它只有单个参数,返回该类型的一个相对应的实例,例如:
Dated= Date . from(instant);
- of 一一聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来,例如:
Set<Rank> faceCards = EnumSet. of (JACK , QUEEN, KING);
- valueOf一一比from 和of 更烦琐的一种替代方法,例如:
Biginteger prime = Biginteger.valueOf(Integer .MAX_VALUE);
- instance 或者getInstance一一返回的实例是通过方法的(如有)参数来描述的,但是不能说与参数具有同样的值,例如:
StackWalker luke = StackWalker.getinstance(options);
- create 或者newInstance一一像instance 或者getinstaηce一样,但create或者newInstance能够确保每次调用都返回一个新的实例,例如:
Object newArray = Array.newInstance(classObject, arrayLen);
- getType 一一像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type 表示工厂方法所返回的对象类型,例如:
FileStore fs = Files.getFileStore(path);
- newType 一一像newInstance一样,但是在工厂方法处于不同的类中的时候使用。Type 表示工厂方法所返回的对象类型,例如:
BufferedReader br= Files.newBufferedReader(path);
- type一一getType和newType的简版,例如:
List<Complaint> litany = Collections.list(legacylitany);
简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解它们各自的长处。静态工厂经常更加合适,因此切忌第一反应就是提供公有的构造器, 而不先考虑静态工厂。