缩进级别

9

你认为多少级缩进是合理的?

我认为C++函数有4/5个或更多级缩进通常是不好的。这意味着你必须一直在脑海中跟踪4/5个或更多的事情。

我的观点是否合理?

(是的,如果完全不进行缩进,就可以避免出现多级缩进:)

12个回答

9

我同意你的观点。如果一个函数包含4或5个以上嵌套的if/switch/loop/try语句,应将其部分内容提取到自己的函数中。

这将使代码更易读,因为提取后的函数名称通常比代码本身更具描述性。


我考虑过这个问题,但是过早地提取仅在一个地方使用的代码是否可以与过早优化放在一起呢?除非代码被重复使用,否则我不会提取它,而只需确保每个代码块都有适当的注释。 - Thomas Owens
1
过早的优化是不好的,因为它往往是浪费时间。如果提取代码使程序更易于理解(从而更易于维护),那么提取代码并不是浪费时间。 - drby
1
@Thomas Owens:正如hackingwords所说,如果代码嵌套太深难以阅读,我认为这不是过早优化,而是提高可维护性。添加注释只会增加冗余信息,而函数名可以更好地传达这些信息。 - Sebastian Dietz

6
这完全取决于您的代码试图解决的问题。有时您别无选择,只能有相当深的缩进级别,尽管这肯定是一种代码异味
我认为你是对的,大约4或5个级别是合理的,如果超过这个数,你可能应该考虑重构该方法。
值得注意的是,人们多年来一直在试图量化代码质量和设计度量标准。其中一个更常见的指标是圈复杂度

2

实际上,最导致代码难以阅读的不是缩进层数,而是你正在查看的模块/函数/方法的长度。

当然,长的代码段通常有更多的缩进层次,因为代码块被嵌入到一行内而不是分开。个人认为,如果一个方法有超过几个屏幕的代码和超过6个缩进层次,那么就会有一种气味。


0

我曾经认为Linus在这方面有点固执,他说不要超过3个层级,否则你的代码就会出问题。但是遵循这个原则在推理控制流方面对我帮助很大。话虽如此,我必须以不同的方式来解释它。直觉上的解释是你应该创建更多微小的函数。我不建议将这个想法发挥到极致,因为这样会使你的控制流和副作用变得难以理解,当所有的副作用都被函数调用所遮盖时,你会被引导到各个地方。

对我帮助最大的是喜欢延迟处理和简单的循环。以这个为例:

// Remove vertices from mesh.
for each vertex in vertices.to_remove:
{
    for each edge in vertex.edges:
    {
        for each face in edge.faces:
        {
             face.remove_edge(edge);
             if (face.size() < 3)
                   face.remove();
        }
        edge.remove();
    }
    vertex.remove();
}

上面那个有四层缩进的庞然大物很尴尬(如果不是伪代码形式,可能还需要更新纹理映射等,需要更多层次的缩进)。我们可以这样做:

for each vertex in vertices.to_remove:
{
    for each edge in vertex.edges:
        edges_to_remove.insert(edge);
}

for each edge in edges_to_remove:
{
    for each face in edge.faces:
        faces_to_rebuild.insert(face, edge);
}

for each face,edge in faces_to_rebuild:
{
    face.remove_edge(edge);
    if (face.size() < 3)
         face.remove();
}

for each edge in edges_to_remove:
    edge.remove();

for each vertex in vertices_to_remove:
    vertex.remove();

虽然这需要对数据进行更多的遍历和一些额外的状态,但它使每个循环变得更简单。当我们有了这些更简单的循环时,它们变得更容易并行化,并且在适当的数据结构下,内存访问模式可以开始采用缓存友好型。

我发现倾向于这种延迟处理不仅减少了缩进,而且简化了我的理解代码的能力,因为控制流非常简单,它们都只会引起一种统一的副作用。这使得更容易看到每个循环正在做什么,并在一眼之间理解它是否正在做正确的事情,因为每个循环都专门用于引起一种类型的副作用,而不是许多不同类型的副作用。令人惊讶的是,尽管需要额外的工作,它通常会改善缓存一致性,并打开多线程和SIMD的新大门,最终获得比开始时更高效的结果。


0

我认为这取决于情况,但通常你会想要减少嵌套。

嵌套循环会影响性能,而嵌套的if语句通常可以被减少到较少的层级。

问题在于你经常会遇到在开发早期过早优化的问题。我认为在进行三层或以上的嵌套之前应该花点时间考虑可能性。


0

我对缩进唯一的不满是当使用两个关键字时,一个依赖于另一个。例如:

switch (c) {
    case 'x':
       foo = bar;

这是不好的,你不能在switch之外有一个case,所以:

switch (c) {
case 'x':
    foo = bar;

..会好得多。在带有if / else if / else的switch语句中,缩进往往会变得混乱。我还强烈建议在缩进时保持80(最多110列限制)。没有人喜欢向右滚动30列以查看您正在做什么,然后向左滚动30列以查看您如何处理结果 :) 更不用说那些必须在服务器上使用愚蠢的80x25控制台模式编辑您的代码的可怜人了。


0

尽管我要说的是从普通C角度来看的,但它可能也适用于C++。我青睐Linux内核编码风格:使用8个字符宽的Tab缩进(自然Tab),并且在80x25终端上适合有多少层缩进(不能超出80个字符宽度)。


0

过多的缩进可能意味着您应该进行重构:创建几个具有良好名称的小方法。这样,您可以将逻辑分解为更小的部分,更容易理解。


0

这与函数应该有多长的问题有关。如果您将函数体保持在十行或更少的长度,那么很难出现太多层次的缩进。

我认为4或5个级别太多了。您几乎可以将其中一些内部循环或条件语句分解成它们自己的函数。

我自己感到有点不安,当缩进达到三个级别时。即使是两个级别也会让我重新考虑我的做法。


0

只要您在编辑器中同时看到所有打开和关闭的括号,多层缩进就没问题。

实际上,这往往会限制您最多使用4个级别。


这不更多指的是编码风格吗?因为较高的屏幕、无空白或内联编排将允许更多的屏幕景观,并意味着与更多空间排版的相同代码相比,该代码 less complex 。 - DevinB
把所有代码放在一个嵌套的if语句里,按照这个度量标准,你会得到28层缩进(我的编辑器在屏幕上显示58行)。我很少使用超过4个缩进,包括方法体的缩进。 - Pete Kirkham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接