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:

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

让我们从一个Java示例开始。假设您希望能够将产品添加到购物车中,可以使用产品ID或Product对象。如果只提供了ID,您希望购物车能够访问产品目录,找到相应的Product对象并将其添加。这就是方法重载可以证明有益的地方(add()方法通过两种不同的签名和实现进行了两次定义)。

class Cart {
  private final List<Product> products;
  void add(int id) {
    this.add(new Catalog().findById(id));
  }
  void add(Product p) {
    this.products.add(p);
  }
}
var c = new Cart();
c.add(new Product("book"));
c.add(42);

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

然而,所解决的问题(代码重复和复杂性)比引入的问题要少。虽然add(Product)方法的语义很明显,但add(int)方法的工作对于其用户来说并不清楚。也许它搜索目录?也许它从现有购物车中选择第n个产品并重新添加到购物车的末尾?或者它可能搜索用户先前下的订单,并从中提取第n个产品?当我们检查方法的签名时,我们无法知道确切情况。

为了理解add(int)方法的作用,我们必须参考其Javadoc块,但该块可能不够准确。此外,正如经常发生的情况一样,Javadoc块中的文档可能与方法内部的代码不同步。简而言之,使用add(int)方法的客户端总是对其内部工作做出假设。如果客户端幸运的话,1) 这个假设将是准确的,并且2) 对该方法的任何后续更改都不会使该假设无效。

似乎方法重载是问题的根源:在一个名称下维护多个实现必然会引入一些隐藏的语义。然后,当某些东西变得模糊不清时,就会引发假设,进而导致误解,最终导致错误。

我相信,一个更好的选择看起来像这样:

class ProductInCatalog implements Product {
  private final Product p;
  ProductInCatalog(int id) {
    this.p = new Catalog().findById(id);
  }
  // all "Product" interface's methods
}
class Cart {
  private final List<Product> products;
  void add(Product p) {
    this.products.add(p);
  }
}
var c = new Cart();
c.add(new Product("book"));
c.add(new ProductInCatalog(42));

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

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

Translated by ChatGPT gpt-3.5-turbo/35 on 2023-09-08 at 16:29

sixnines availability badge   GitHub stars