高效 Java 星期二!探讨静态工厂方法

2025-06-09

高效 Java 星期二!探讨静态工厂方法

最近我一直在读Joshua Bloch的《Effective Java》(Addison-Wesley出版社,2018年出版)。如果你是一名Java开发者,希望这不是你第一次听说这本书。或许你和我一样,第一次看到这本书时会想:“这本书可能已经过时了。” 的确,它的第一版是在21世纪初出版的。然而,在听到许多好评后,我决定尝试一下。不用说,我惊喜地发现了它。唉,这也没什么好奇怪的,好的设计其实永远不会过时(我们到现在还在谈论“四人帮”,不是吗?)

考虑到所有这些,为什么我要写这个系列?说实话,说句自私的话,首要原因是为了我自己。我坚信学习的最好方式就是去教。即便如此,我依然热爱优秀的设计,并乐于分享这份热爱,希望能帮助其他人学习新知识并付诸实践。因此,我决定尝试借鉴书中的一些原则,每周分享一条新的。尽量给自己减压,同时也给自己一个更新的节奏。

最后,这一系列会取代原著吗?完全不会,我仍然强烈建议你去看看原著。希望它能与原著相辅相成,让你对原著中分享的主题有不一样的视角。

不用多说,我们开始吧。

第一章:静态工厂与构造函数

开始学习面向对象编程 (OOP) 时,首先要学习的就是如何创建对象。如果无法创建对象,那么拥有所有这些对象也没什么用。我们通常学习创建对象的方式是通过构造函数。构造函数相当简单直接,所以这很有意义,而且可以很好地服务于我们。作者建议的另一个选择是静态工厂方法,但许多程序员在应该尝试的时候却没有尝试过。那么什么是静态工厂方法呢?简单来说,静态工厂方法就是返回类实例的静态方法。那么它是什么样子的呢?

LocalDateTime date = new LocalDateTime(Instant.now());
Enter fullscreen mode Exit fullscreen mode

对比

LocalDateTime date = LocalDateTime.ofInstant(Instant.now());
Enter fullscreen mode Exit fullscreen mode

两者之间肯定没有太大区别。那么工厂方法提供了什么好处呢?

工厂方法可以有名称

我刚开始开发软件的时候,似乎并不理解良好命名的威力。构造函数无法被命名。因为它们不能使用不同的名称,所以你也不能有两个构造函数以相同的顺序接收相同的参数,执行不同的操作。例如,如果我们想在 LocalDateTime 类中添加一个构造函数,该构造函数接受一个 Instant 参数,并生成一个比给定 Instant 参数早的随机时间 LocalDateTime 值,那么该如何用构造函数来实现呢?可以改成类似这样吗?

LocalDateTime dateTime = new LocalDatetime(Instant.now(), shouldBeBefore);
Enter fullscreen mode Exit fullscreen mode

绝对不是。但是使用工厂方法,我们可以写如下代码:

LocalDateTime datetime = LocalDatetime.randomTimeBeforeInstant(Instant.now());
Enter fullscreen mode Exit fullscreen mode

干净多了。

程序员会用的另一个技巧是改变参数的顺序,这可能不像上面的例子那么粗暴,但对于未来的开发人员来说,很难知道哪个参数起了什么作用。因此,这对可维护性来说不是一个很大的好处。

工厂方法不需要每次都创建新对象

构造函数本质上每次调用都需要创建一个新对象。方法则不然。想想单例模式,这个大多数人都熟悉的模式。实际上,它是一种利用静态工厂方法的模式!getInstance单例模式中方法的真正目的不是创建多个实例。还有其他选择,例如返回缓存值或枚举。

工厂方法可以返回方法返回类型的任何子类型的对象

这是一个很棒的功能。能够将此需求与接口分离,为我们提供了更多选择。这也使我们能够根据传递给函数的参数返回不同的子类型。因此,我们不仅可以返回子类型,还可以在运行时动态选择要返回的子类型。进一步扩展,我们甚至可以返回工厂方法编写时不存在的类的实例。同样,这会带来更多选择。好的设计通常就是为各种选择留有余地。

那么工厂方法有哪些缺点呢?

它们影响我们使用继承的能力

如果你只向类的用户提供工厂方法,而没有公共构造函数,那么该类就不可能使用继承。这也可以看作是一种好处(final 类已经实现了这一点)。这只是又一次考虑组合优于继承的时刻。在使用此方法时,我们必须牢记这一点。

在公共 API 中可能更难找到它们

由于构造函数比较特殊,它们会跳出文档和类的范围。那么,我们如何降低这种风险呢?一个可靠的方法是遵循工厂方法的命名约定。例如,fromofgetInstance

凯尔的看法:

我认为这是工具带里一个很实用的工具。虽然我仍然很难每次都用它作为我工作的基本案例,但我正在努力记住这一点。模式的另一个有趣之处在于,在开发几年后,你会发现自己偶然发现了这些模式,并在不知不觉中就用上了它们。这个模式也不例外。在我之前的开发生涯中,我曾使用过单例模式以及更通用的静态工厂方法。听到你做的事情是别人真正用过的模式,总是让人感到安心。

你呢?你以前用过这个模式吗?效果怎么样?

鏂囩珷鏉ユ簮锛�https://dev.to/kylec32/ effective-java-tuesday-let-s-consider-static-factory-methods-170p
PREV
使用 Github Actions 将静态网站部署到 AWS
NEXT
我的 macOS 开发环境工具 终端工具 非终端工具 工具≠实践