假定包含以下方法的接口:
Car find(long id);
List<Car> find(String model);
像这样重命名它们更好吗?
Car findById(long id);
List findByModel(String model);
实际上,使用此API的任何开发人员都无需查看接口即可知道初始find()
方法的可能参数。
所以我的问题更笼统:在代码中使用重载方法有什么好处,因为它会降低可读性?
假定包含以下方法的接口:
Car find(long id);
List<Car> find(String model);
像这样重命名它们更好吗?
Car findById(long id);
List findByModel(String model);
实际上,使用此API的任何开发人员都无需查看接口即可知道初始find()
方法的可能参数。
所以我的问题更笼统:在代码中使用重载方法有什么好处,因为它会降低可读性?
Answers:
与您可能会遇到的许多其他不良可读性实践相比,这是一个相对较小的问题,因此我想说,如何命名方法主要取决于口味。
话虽如此,如果您要对此做点什么,我将遵循以下做法:
如果...超载
这些方法几乎遵循相同的合同,但仅在不同的输入上操作(想象一个电话接线员可以通过您的个人税号,您的帐号或您的姓名和生日查询您的帐户)。这包括返回相同类型的输出。
如果使用其他名称...
这些方法做的事情大不相同,或者返回不同的输出(例如您的情况)。如果一个人访问数据库而另一个人不访问数据库,则可以考虑使用其他名称。
另外,如果返回的类型不同,我还将更改动词以表明:
Car findById(long id);
List findAllByModel(String model);
在每种情况下,我建议使用其他名称。这有可能是在未来的一段时间,你会希望添加其他的方法,比如List<Car> findByMake(String make)
,对比List<Car> findByModel(String model)
。所以突然间,一切都find
变得毫无意义了。如果您的方法名称提供了有关应如何使用它们的更多信息,则也不太可能无意中使用了错误的方法。
find(Make val)
和显式表示,那么这将不成问题find(Model val)
。然后,诸如此类的便捷方法findByMake(String val)
将更加清楚其实际功能。毕竟,a String
既不是品牌,也不是模型,因此该方法应解释其实际作用。
如果重命名方法,它将不再重载。就其本身而言,重载并不一定会使代码的可读性降低,但是如果语法不明确,重载可能会使实现更难以遵循。
许多语言都使用方法重载来表示功能接口的功能,其中参数可能是可选的,并且隐含了可选参数的默认值。对于在方法声明中不支持默认参数语法的语言,尤其如此。
这样做:
void MyMethod(int param1, int param2 = 10)
{
...
}
避免您这样做:
void MyMethod(int param1)
{
MyMethod(param1, Param2Default);
}
void MyMethod(int param1, int param2)
{
....
}
至于哪个更具可读性,那实际上取决于您。我个人更喜欢第二种选择,特别是在参数列表过长的时候,但是我想只要您在整个API中保持一致就没有关系。
当您希望函数执行基本相同的操作,并且希望参数列表相同但返回类型不同时,重载就会遇到困难。大多数语言不知道如何区分两个相同但返回类型不同的方法。此时,您需要考虑使用泛型,更改参数接口或重命名其中一种方法以指示返回类型的差异。如果您不希望采用简单明了的命名方案来处理此类情况,那么可读性就可能成为一个大问题。
命名重载的方法GetSomething()
,GetSomethingEx()
并不会过多地说明方法之间的区别,特别是如果返回类型是它们之间的唯一区别时。另一方面,GetSomethingAsInt()
并GetSomethingAsString()
告诉您更多有关方法在做什么的信息,尽管这并不是严格意义上的重载,但它确实表明这两种方法执行相似的操作,但是返回不同的值类型。我知道您可以使用其他方法命名方法,但是为了说明这一点,这些粗略的示例应该可以做到。
在OP的示例中,重命名并不是严格必需的,因为方法参数不同,但是确实可以更清楚地命名一个方法。最后,它实际上取决于您希望提供给用户的界面类型。是否不超载的决定不应仅基于您自己对可读性的理解。重载方法可以例如简化API接口并减少开发人员可能需要记住的方法数量,另一方面,它可以使接口模糊不清,从而需要开发人员阅读方法文档以了解哪种格式。使用方法,而拥有许多类似但描述性地命名的方法,则可以使方法更加清晰,只需阅读方法名称即可。
只要方法返回相同的内容并遵循相同的约定,就支持重载。重载使调用代码免于不必要地提交给参数类型。
假设调用函数接收搜索查询作为参数,并在调用之前和/或之后执行其他一些处理find
。
void tryToSellCars(String which) {
/* grab an airhorn, inflatable tube guy... */
List<Car> cars = find(which);
/* expound virtues of each car in detail... */
}
如果您出于某种原因要更改查询的类型(例如,从简单的ID字符串更改为某种功能齐全的查询对象),则只需更改函数签名即可在调用函数中进行更改接受新的参数类型,而不必担心更改它在您的类上调用的方法。
void tryToSellCar(CarQuery which) {
/* grab airhorn, inflate tube guy... */
List<Car> cars = find(which)
/* expound virtues of each car in detail... */
}
如果单独实施findById
,findByQueryObject
则必须查找每个调用以进行更改。在示例中,我只更改了一个字就完成了。
findByFoo
更早地捕获类型不匹配。