静态工厂方法


  • 静态工厂方法是什么
  • 静态工厂方法的声明方式
  • 静态工厂方法的优势
  • 静态工厂方法的劣势
  • 静态工厂方法的争议

 静态工厂方法与构造器不同的第一大优势在于:他们有名

静态工厂方法与构造器不同的第二大优势在于:不必在每次调用的时候都创建一个新的对象
静态工厂方法与构造器不同的第三大优势在于:他可以返回原类型的任何子类型的对象
静态工厂方法与构造器不同的第四大优势在于:在创建参数化类型实例的时候,他们使代码变得更加的整洁

静态工厂方法的主要缺点在于
1:类如果不含公有或者被保护的构造器就不能被实例化
2:他们与其他的静态方法其实没有什么不一样。因此对于提供了静态工厂方法而不是构造器的类来说,要查明如何实现一个类是非常困难的。
静态工厂方法的一些管用名称
valueOf—— 类型转换方法
of——valueOf的简写形式
getInstance——返回的实例通过方法参数描述,但不能够说与参数具有相同的值 对于Singleton,该方法没有参数返回唯一的实例
newInstance ——与getInstance一样 单返回的实例与所有其他的实例都不一样
getType—— 与getInstance一样 但是工厂方法处于不同的类中的时候使用 type 表示对象的类型
newType——与newInstance一样但是 工厂方法处于不同的类中的时候使用 type 表示类型

静态工厂方法是什么

首先,不要把静态工厂方法和设计模式中的几种工厂模式相混淆了。两者没有必然的关联,静态工厂方法只是一个静态的,用来返回当前类型(或者其子类型)的实例的一个方法。而工厂模式则涉及到更多的概念,相关概念请参考设计模式中的相关文章。

 

当然,仔细想想,它们之间也存在一些共同点的,它们都是为了更加灵活的创建对象。而且当静态工厂方法声明的返回类型是接口类型的时候,该静态工厂方法的作用就神似简单工厂模式这一基础的创建类设计模式了。

 

静态工厂方法的声明方式

为什么要讨论声明方式,因为静态工厂方法的声明方式和其他静态方法的声明方式几乎没有什么区别。所以,要识别或者定义一个静态工厂方法,更多的需要人为的设置一些约定。主要体现在命名方式上,几种常见的命名方式如下:

valueOfofgetInstancenewInstancegetTypenewType

 

以上的方法中,getInstance的出镜率很高,在单例类中经常会定义一个getInstance方法用来返回该类型的唯一实例。而valueOf方法和of方法则往往用来解析传入的参数,然后根据它来返回对应的实例。这两个方法常用在多例类中,所谓多例类,就是诸如枚举类型等实例个数不唯一,但是数量有限的类。最常见的例子比如Boolean类,其中定义了一个valueOf方法,接受字符串类型的truefalse,然后返回Boolean.True或者Boolean.FalsenewInstance在反射API中会用到,比如通过调用类的class对象上的newInstance方法来得到一个该类的实例。

 

除了命名方式外,方法的访问修饰符可以根据需要进行设置,这一点和普通方法一致。一般情况下,设置为public即可,因为静态工厂方法的主要用意也是被调用以获取类的实例。

 

另外,既然静态工厂方法是一个普通方法,那么它就必须有返回值。通常而言,这个返回值的声明类型应该是此静态工厂方法所在类的类型。

 

静态工厂方法的优势

 

当然,这里所说的优势,通常都是和传统构造函数相比得到的。

 

静态工厂方法拥有名字

静态工厂方法拥有自己的名字,而构造函数则没有。这也就意味着,同一组参数列表,只能有唯一的一个构造函数(通过改变参数列表中参数的顺序,可以有多个构造函数,但是很明显,这不是一个好主意)。相比之下,静态工厂方法则没有这个限制,对同一组参数,可以有任意多的静态工厂方法。因此,这种情况下使用静态工厂方法来进行对象实例化更加灵活,可读性也更好。

 

对于对象实例化的更多控制

对于构造函数而言,一旦它被调用,那么就肯定会生成一个新的实例。而调用静态工厂方法则不一定,它只需要返回一个对象的实例就够了,至于这个返回的对象是如何得到的,它并不关注。因此,静态工厂方法对于对象的实例化就有了更多的控制权。它能够利用预先创建好的不可变对象(Immutable Instance),也能够从缓存中获取某个经常被请求的对象。从而大幅度的提高性能。

 

返回对象的类型更加泛化

对于构造函数而言,它只能返回当前类的实例。而静态工厂方法的实际返回类型可以是声明返回类型的任何子类型。比如,声明返回Base类型的静态工厂方法,实际上它能够返回Sub类型,只要SubBase的子类型。因为子类型的概念不仅限于继承层次,还可以适用在接口和其实现类之间,因此这种泛化的关系也非常契合“面向接口编程”这一指导原则。

 

静态工厂方法能够使用类型推导

要实例化一个泛型类,有可能要写非常冗长的初始化语句,例如:

Map<String,List<String>> m =newHashMap<String, List<String>>();

 

其实这还算是好的,如果你需要使用一个嵌套了若干层的HashMap,那么写对它的实例化代码还真不那么轻松,这个时候,可以考虑使用静态工厂方法进行一次封装,借助它的类型推导特性:

publicstatic <K, V> HashMap<K, V> newInstance() {

return new HashMap<K, V>();

}


然后,通过下面的代码来创建:

Map<String,List<String>> m = HashMap.newInstance();

 

当然,对于在这个场景下使用类型推导,究竟是好是坏,好的方面是它确实减少了代码量,但是坏的方面也很明显,它的可读性没有那么强,容易让人迷惑。

 

关于类型推导,在《Java编程思想,第四版》的第15章的15.4.1小节有介绍。

 

静态工厂方法的劣势

 

静态工厂方法和普通静态方法没有区分度

如果不靠命名规范,那么区分静态工厂方法和普通静态方法还是不那么容易的,至少不能一眼就看出来。所以,在目前缺少JavaDoc和内置语言特性支持的情况下,使用靠谱的命名规范是十分重要的,关于命名的建议,在前文也有描述。

 

静态工厂方法的争议

如果一个类型中,提供静态工厂方法而不提供public或者protected的构造函数,会使该类型不可被继承。继承依赖于构造函数,这是因为在使用子类型的构造函数的时候,会调用父类型的构造函数。

 

注意前面的“仅”字,一般而言,上面这种情况是不会发生的,除非是特意为之,比如Java Collection Framework中的Collections类中的那些包装方法,它完全就是一个工具类,不需要实例化,也不需要存在于继承层次上。

 

在《Effective Java》中,这个争议实际上被归为静态工厂方法的最主要的一个弊端,但是貌似作者本人也不是很认同这一点,原文中提到了:

 

Forexample,it is impossible tosubclass any of the convenience implementation classes in theCollections Framework. Arguably this can bea blessing in disguise, as it encouragesprogrammers to use composition instead of inheritance.

比如,不能实例化JavaCollections Framework中的任何实现类。但是这确是一个暗地里的好处,因为它鼓励我们去实现组合而非继承。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值