The Hidden Dangers of Method Overloading

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

方法重载是许多编程语言中的一种常见特性,允许一个类拥有两个或更多具有相同名称但参数不同的方法。根据Microsoft的说法,方法重载是”改善可重用库的可用性、生产率和可读性的最重要技术之一”。我不同意这一观点。在我看来,方法重载可能导致代码可读性降低和出现更多的错误,因为在相同名称下维护两个或更多的实现会引发隐藏的语义,这必然会导致误解和功能缺陷的产生。

让我们以Java示例开始。假设您希望能够将产品添加到购物车,既可以使用产品ID,也可以使用Product对象。如果只提供了ID,您希望购物车能够访问产品目录,找到相应的Product对象并将其添加进去。这就是方法重载可以发挥作用的地方(add()方法被定义了两次,具有不同的签名和实现)。

这种方法确实有至少两个方面的便利。首先,add(int)方法处理了从intProduct的转换,不需要在其他地方重复这个操作,它们只需将产品的ID传递给这个方法,让它完成工作,从而消除了代码重复。其次,因为“搜索目录”的功能没有暴露到Cart类以外,简化了周围的代码。看起来,代码的可用性和可读性确实得到了增强。

然而,这些问题(代码重复和复杂性)相对于引入的问题来说是次要的。虽然add(Product)方法的语义很明显,但add(int)方法的工作方式对用户来说并不清楚。也许它搜索目录?也许它从现有购物车中选择第n个产品并重新添加到购物车末尾?或者它从用户之前下的订单中搜索并提取第n个产品?当我们检查该方法的签名时,我们根本不知道。

为了理解add(int)方法的功能,我们必须参考它的Javadoc块,但这可能不够准确。此外,正如经常发生的那样,Javadoc块中的文档可能与方法内部的代码不同步。直截了当地说,使用add(int)方法的客户端不可避免地对其内部工作方式做出假设。如果客户端很幸运,1)假设是准确的,2)对方法进行的任何后续更改都不会使该假设失效。

看起来,方法重载是问题的根源:在一个名称下维护多个实现必然会引入某些隐含的语义。然后,当某些事物被模糊化时,它会引发假设,进而导致误解,最终导致错误。

我认为,一个更好的替代方案如下:

ProductInCatalog的构造函数不是无代码的,尽管理想情况下应该是这样的,然而,这一点对我们当前的讨论来说并不那么重要。ProductInCatalog类用作目录中找到的Product的抽象。这个类由Cart类的客户端使用。客户端在完全意识到并明确意图的情况下,将42(产品的ID)转换为ProductInCatalog的实例。这个设计不再隐藏任何元素。没有任何假设需要做出,也没有代码必须满足的条件。

我们是否仍然保留了方法重载提供的优势呢?确实如此。没有代码重复,代码的复杂性也减少了。此外,使用Cart的代码的可读性显著提高了。总之,我建议避免方法重载,尽管这无疑会导致代码库中类的数量增加。然而,这是一个完全不同的讨论。

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-28 at 15:47

sixnines availability badge   GitHub stars