The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
我相信Joshua Bloch在他的非常好的书《Effective Java》中首次提到:与构造函数相比,静态工厂方法是实例化对象的首选方式。我不同意。不仅仅是因为我认为静态方法是纯粹的邪恶,而且主要是因为在这种特定情况下,它们假装是好的,让我们误以为我们必须喜欢它们。
让我们从面向对象的角度来分析这个推理,看看为什么是错误的。
这是一个具有一个主要构造函数和两个次要构造函数的类:
这是一个类似的类,具有三个静态工厂方法:
你更喜欢哪一个?
根据Joshua Bloch的说法,使用静态工厂方法而不是构造函数有三个基本优势(实际上有四个,但第四个对Java不适用了)。
They can cache.
They can subtype.
我相信这三个都是有道理的……如果设计是错误的话。它们是绕过问题的好借口。我们一个一个来看。
这是使用构造函数创建红色番茄颜色对象的方法。
这是使用静态工厂方法的方式:
似乎makeFromPalette()
比仅new Color()
在语义上更丰富,对吗?是的。如果我们只是将这三个数字传递给构造函数,谁知道这些数字代表什么意思呢?但是,“palette”一词可以帮助我们立即弄清楚一切。
然而,正确的解决方案应该是使用多态性和封装,将问题分解为几个语义丰富的类:
现在,我们使用正确的类的正确构造函数:
See, Joshua?
They Can Cache
假设我需要在应用程序的多个地方使用红色番茄颜色。
将创建两个对象,这显然是低效的,因为它们是相同的。最好将第一个实例保存在内存中,并在第二次调用时返回它。静态工厂方法可以解决这个问题。
然后,在Color
内部的某个地方,我们保留一个私有的静态Map
,其中包含所有已经实例化的对象。
它在性能方面非常有效。对于像我们的“Color”这样的小对象,问题可能不太明显,但是当对象变得更大时,它们的实例化和垃圾回收可能会浪费很多时间。
然而,有一种面向对象的方法可以解决这个问题。我们只需要引入一个新的类Palette
,它成为了一个颜色存储器。
现在,我们只需创建一个Palette
的实例,并在每次需要颜色时向它请求即可。
看,Joshua,没有静态方法,没有静态属性。
假设我们的类Color
有一个方法lighter()
,它应该将颜色转换为下一个可用的较浅颜色:
然而,有时更希望通过一组可用的Pantone颜色选择下一个较浅的颜色。
然后,我们创建一个静态工厂方法,该方法将决定哪种Color
实现对我们最合适。
如果需要真正的红色,我们将返回一个PantoneColor
的实例。在所有其他情况下,它只是一个标准的RGBColor
。这个决定是由静态工厂方法做出的。这是我们将如何调用它的方式:
不可能通过构造函数进行相同的“分叉”,因为它只能返回声明它的类。静态方法具有所有必要的自由来返回Color
的任何子类型。
然而,在面向对象的世界中,我们可以并且必须以完全不同的方式进行操作。首先,我们将Color
定义为一个接口:
接下来,我们将把这个决策过程移动到自己的类Colors
中,就像我们在之前的示例中所做的一样。
而且我们将使用Colors
类的实例,而不是在Color
内部使用静态工厂方法。
然而,这仍然不是真正的面向对象的思维方式,因为我们将决策从它所属的对象中剥离出来。通过静态工厂方法make()
或一个新的类Colors
—无论如何都无关紧要—我们将对象分成两部分。第一部分是对象本身,第二部分是决策算法,它留在其他地方。
一个更加面向对象的设计是将逻辑放入PantoneColor
类的对象中,它将装饰原始的RGBColor
对象。
然后,我们创建一个RGBColor
的实例,并使用PantoneColor
进行装饰。
我们要求 red
返回一种较浅的颜色,它返回的是 Pantone 调色板中的颜色,而不是仅仅在 RGB 坐标上较浅的颜色。
当然,这个例子相对简单,并且如果我们真的希望它适用于所有的 Pantone 颜色,还需要进一步改进,但我希望你能理解。逻辑必须留在类的内部,而不是在外部的某个地方,也不是在静态工厂方法中,甚至不是在其他一些补充类中。当然,我说的是属于这个特定类的逻辑。如果与类实例的管理有关,那么可以有容器和存储,就像上面的示例一样。
总结一下,我强烈建议永远不要使用静态方法,特别是当它们将替代对象构造函数时。通过构造函数创建对象是面向对象软件中最“神圣”的时刻,不要错过它的美。
Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-17 at 14:41