34

是编写许多小方法(或函数)更好,还是简单地将这些小进程的逻辑/代码直接写入您将调用小方法的地方?即使暂时只从一个地方调用它,将代码分解成一个小函数又如何呢?

如果一个人的选择取决于某些标准,它们是什么?程序员应该如何做出好的判断?

我希望答案可以普遍适用于多种语言,但如有必要,给出的答案可以特定于一种或多种语言。特别是,我想到了 SQL(函数、规则和存储过程)、Perl、PHP、Javascript 和 Ruby。

4

12 回答 12

44

我总是将长方法分解成逻辑块,并尝试从中制作更小的方法。除非我在两个不同的地方需要它,否则我通常不会将几行代码变成一个单独的方法,但有时我这样做只是为了提高可读性,或者如果我想单独测试它。

Fowler 的重构就是关于这个主题的,我强烈推荐它。

这是我在重构中使用的一个方便的经验法则。如果一段代码有一个注释,我可以将它改写为方法名称,将其拉出并使其成为方法。

于 2008-10-30T14:12:59.383 回答
13

该方法的大小与其圈复杂度直接相关。

保持方法尺寸较小(这意味着将一个大方法分成几个小方法)的主要优点是:

  • 更好的单元测试(由于低圈复杂度)
  • 由于更明确的堆栈跟踪(而不是一个巨大的方法中的一个错误),更好的调试
于 2008-10-30T14:20:00.743 回答
9

一如既往,您可以说:这取决于。这更多的是命名和定义方法任务的问题。每种方法都应该完成一个(而不是更多)定义明确的任务,并且应该完整地完成它们。方法的名称应该指明任务。如果您的方法名为 DoAandB(),最好使用单独的方法 DoA() 和 DoB()。如果您需要 setupTask、executeTask、FinishTask 等方法,将它们组合起来可能会很有用。

一些要点表明,不同方法的合并可能有用:

  • 一种方法不能单独使用,而不使用其他方法。
  • 您必须小心以正确的顺序调用一些依赖方法。

一些点表明,该方法的拆分可能是有用的:

  • 现有方法的某些行具有明确的独立任务。
  • 大方法的单元测试会出现问题。如果为独立方法编写测试更容易,那么将大方法拆分。

作为对单元测试参数的解释:我写了一个方法,它做了一些事情,包括 IO。IO部分很难测试,所以我考虑了一下。我得出的结论是,我的方法做了 5 个合乎逻辑且独立的步骤,其中只有一个涉及 IO。所以我将我的方法分成 5 个较小的方法,其中四个很容易测试。

于 2008-10-30T14:14:39.880 回答
7

每次都是小方法。

他们是自我记录的(呃,如果名字好的话)

他们将问题分解为可管理的部分——你就是 KeepingItSimple。

您可以使用 OO 技术更容易(并且明显地)插入行为。根据定义,大方法更具程序性,因此灵活性较低。

它们是可单元测试的。这是杀手锏,你根本无法对一些执行大量任务的巨大方法进行单元测试

于 2008-10-30T14:58:52.223 回答
4

我从 The Code Complete 书中学到的东西:

  • 编写方法/函数,使其实现一个逻辑块(或单元或任务)。如果这需要分解为子任务,那么为它们编写一个单独的方法/函数并调用它们。
  • 如果我发现方法/函数名称越来越长,那么我会尝试检查该方法以查看它可以分为两种方法。

希望这可以帮助

于 2008-10-31T15:18:32.633 回答
3

我发现拥有许多小方法可以使代码更易于阅读、维护和调试。

当我阅读实现某些业务逻辑的单元时,如果我看到一系列描述流程的方法调用,我可以更好地遵循流程。如果我关心方法是如何实现的,我可以去看看代码。

感觉像是更多的工作,但最终节省了时间。

我认为,知道封装什么是一门艺术。每个人都有一些细微的意见分歧。如果我可以用文字来表达,我会说每种方法都应该做一件可以被描述为完整任务的事情。

于 2008-10-30T14:21:45.530 回答
2

一些经验法则:

  • 功能不应长于屏幕上可显示的内容
  • 如果它使代码更具可读性,则将函数分解为更小的函数。
于 2008-10-30T14:15:27.820 回答
2

我让每个函数做一件事,而且只做一件事,并且我尽量不嵌套太多的逻辑层级。一旦您开始将代码分解为命名良好的函数,它就会变得更容易阅读,并且实际上是自记录的。

于 2008-10-30T14:17:32.480 回答
1

方法越大,测试和维护就越困难。我发现当一个大过程分解成原子步骤时,它更容易理解它是如何工作的。此外,这样做是使您的类可扩展的重要的第一步。您可以将这些单独的步骤标记为虚拟的(用于继承),或将它们移动到其他对象(组合)中,从而使您的应用程序的行为更易于定制。

于 2008-10-30T14:16:25.117 回答
1

我通常会将函数拆分为较小的函数,每个函数执行一个单一的原子任务,但前提是该函数足够复杂以保证它的安全。

这样,我最终不会为简单的任务提供多个功能,而且我提取的功能通常可以在其他地方使用,因为它们不会尝试实现太多。这也有助于单元测试,因为可以单独测试每个功能(作为逻辑的原子操作)。

于 2008-10-30T14:34:34.207 回答
1

这有点……取决于心态。不过,这不是一个固执己见的问题。

答案实际上取决于语言上下文。

在 Java/C#/C++ 世界中,人们遵循 Robert Martin 所宣扬的“清洁代码”学派,那么:许多小方法是必经之路。

一个方法有一个明确的名字,只做一件事。一层嵌套,就是这样。这将其长度限制为 3、5、最多 10 行。

老实说:我发现这种编码方式绝对优于任何其他“风格”。

这种方法的唯一缺点是您最终会得到许多小方法,因此在文件/类中进行排序可能会成为问题。但答案是使用一个体面的 IDE,它可以轻松地来回导航。

因此,使用“所有东西都融入一个方法/功能”的唯一“合法”理由是当您的整个团队都这样工作并且更喜欢这种风格时。或者当你不能使用像样的工具时(但是导航那个又大又丑的功能也不起作用)。

于 2018-11-30T19:45:37.987 回答
0

就个人而言,我明显倾向于使用更多、更小的方法,但并没有达到虔诚地追求最大行数的地步。我的主要标准或目标是保持我的代码干燥。一旦我有一个重复的代码块(无论是在精神上还是实际上是由文本),即使它可能是 2 或 4 行长,我也会将该代码干燥到一个单独的方法中。有时,如果我认为将来很有可能再次使用它,我会提前这样做。

另一方面,我也听到有人争论说,如果你的中断方法太小,在一个开发团队的环境中,一个队友很可能不知道你的方法,要么写内联,要么写他自己做同样事情的小方法。诚然,这是一个糟糕的情况。

有些人还试图争辩说将内容保持内联更具可读性,因此读者可以自顶向下阅读,而不必跳过可能跨越多个文件的方法定义。就个人而言,我认为堆栈跟踪的存在使这不是什么大问题。

于 2008-10-30T14:15:56.397 回答