为什么用静态工厂替代构造函数?

22-08-08 banq

为什么我们不能再有正常的、简单的构造函数呢?
每个JDK的新功能都会使用那些恼人的 "of(value)"、"newAbcd()"、"of()"。
在某些情况下,我同意需要使用它,例如接口(Path.of()),但我觉得它真的被过度使用了。
如果只是 "of()",没有参数,那就更奇怪了,"of "这个词不应该被使用(HexFormat.of())。
而当使用newAbcd()的样式时,会变得非常长:HttpClient httpClient = HttpClient.newHttpClient(); ...
现在,没有参数的 "of() "与 "newAbcd() "也有不一致的地方。

JDK9废止了一个超级通用的构造函数而改用静态工厂方法,我真的不喜欢这样。
在JDK 19中,他们已经废弃了new Locale(),增加了Locale.of()。
我明白这是为了缓存,但我真的觉得这不是一个好方法,它只是在不同的类中增加了很多不一致的情况。

我更喜欢大多数东西只是new Abcd()的时候。

静态工厂本身具有描述性
Instant就是这种模式的一个正确例子。Instant.now()清楚地表明了你得到的东西,ofEpochSecond/ofEpochMili也是如此(另外,由于两者都接受一个long,它们无论如何不能作为重载构造函数存在)。

至于HexFormat。是的,of()不是一个好名字。但它不能把构造函数公开,因为它是一个基于值的类。
一旦项目Valhalla到来,这些就会变成值类,不幸的是,这样做是对构造函数的字节码不兼容的改变。因此,他们确保JDK中所有基于值的类都有私有构造函数。

构造函数缺点
构造函数不够灵活。你可能想用另一个类来实现。你可能想提供更具描述性的名字。你可能想做一些缓存。也许现在不需要,但在不破坏合同的前提下,保持这种可能性。

现在我并不鼓励大家在自己的代码中用工厂来取代构造函数。但对于库来说,这可能是有意义的。

静态工厂好处
这个话题在Effective Java中得到了很好的覆盖。

关于静态工厂方法的总结:

优点

  • 它们可以有描述性的名字
  • 它们不需要创建一个新的对象(如Locale缓存的例子)。
  • 它们可以返回其返回类型的任何子类型
  • 返回的类可以根据输入参数而变化
  • 在编写方法时,返回对象的类不需要存在(如服务提供者框架)。


限制
  • 没有构造函数的类不能被子类化


of取名问题
of()看起来很奇特,但这是一个典型的实用主义权衡的例子。
由于new关键字的存在,他们不能使用new(),所以他们必须选择别的方法。
对于非零参数的工厂方法,of(...)往往是new()的一个相当安全和合理的替代品,因为实现不需要一个专门的名字。
由于of(...)已经被接受为new(...)的替代品(而且已经被接受了很多年),所以没有太多理由花费额外的精力去寻找new()这个特殊情况的可接受的替代品,所以of()被类似地重复使用。
此外,由于没有选择其他的东西,所有的of*(*)名称都会在标识符列表中被分组,也许最重要的是在完成建议中。即使他们可以修改语言以允许new()方法,这也不是一个好主意,因为new操作符有常规方法所不提供的特殊保证。

of()孤立地看不是一个好名字,但在实践中却是一个很好的名字。
 

1