Class Casting Is a Discriminating Anti-Pattern

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

类型转换是一种非常有用的技术,当没有时间或意愿去仔细思考和设计对象时,它可以派上用场。类型转换(或类转换)可以帮助我们根据对象所属的类或实现的接口,以不同的方式处理提供的对象。类转换可以帮助我们对贫穷的对象进行区分,并根据它们的种族、性别和宗教分开它们。这是否是一种好的实践呢?

这是一个非常典型的类型转换示例(例如,Google Guava中充满了这种技术,比如Iterables.size())。

sizeOf() 方法用于计算可迭代对象的大小。然而,它足够智能,能够理解如果 items 也是 Collection 的实例,就无需实际迭代它们。将它们转换为 Collection 并调用 size() 方法会更快。这看起来很合理,但这种方法有什么问题呢?我认为有两个实际问题。

首先,sizeOf()Collection 之间存在 隐藏的耦合。这种耦合对于 sizeOf() 的客户端是不可见的。他们不知道 sizeOf() 方法依赖于接口 Collection。如果我们明天决定更改这个接口,sizeOf() 将无法工作。而且我们会感到非常惊讶,因为它的签名对这种依赖没有任何说明。当然,这在 Collection 中不会发生,因为它是 Java SDK 的一部分,但在自定义类中,这种情况可能会发生,而且确实会发生。

第二个问题是 sizeOf() 方法的 不可避免的复杂性增加。它必须处理越来越多的特殊类型,它的复杂性也会随之增加。这种 if/then 分支是不可避免的,因为它必须检查所有可能的类型并给予它们特殊处理。这种复杂性违反了单一责任原则。该方法不仅计算 Iterable 的大小,还执行类型转换并根据该转换进行分支。

那么有什么替代方案呢?有几种选择,但最明显的是方法重载(在 Ruby 或 PHP 等非完全面向对象的语言中不可用)。

那不是更优雅吗?

从哲学角度来看,类型转换是对进入方法的对象的歧视。该对象遵守方法签名提供的合同。它实现了Iterable接口,这是一个合同,它期望与进入同一方法的所有其他对象受到平等对待。但是,该方法通过对象的类型来区分它们。该方法基本上在询问对象属于什么种族。黑色对象向右走,白色对象向左走。这就是instanceof的作用,也是歧视的本质。

通过使用instanceof,该方法将进入的对象按照它们所属的特定组别进行分离。在这种情况下,有两个组别:集合和其他所有对象。如果你是一个集合,你会得到特殊待遇。即使你遵守了Iterable的合同,我们仍然会特别对待某些对象,因为它们属于一个被称为Collection的“精英”组。

你可能会说Collection只是另一个对象可能遵守的合同。这是对的,但在这种情况下,那些按照该合同工作的对象应该通过另一扇门进入。你宣布sizeOf()接受所有遵守Iterable合同的对象。我是一个对象,我按照合同做事。我进入方法,并期待与进入同一方法的其他所有对象一样受到平等对待。但是,显然,一旦进入方法,我意识到一些对象有一些特权。那不是歧视吗?

总之,我认为instanceof和类转换是反模式和代码异味。一旦你发现需要使用它们,就应该开始考虑重构。

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-17 at 17:10

sixnines availability badge   GitHub stars