if语句或for循环中的长代码块

时间:2009-12-04 12:18:28

标签: refactoring formatting coding-style

这是关于编码风格的跨语言问题。

我必须使用很多代码,这些代码在if语句或for循环中有很长的代码块,有时甚至数百行。代码是程序性的。

代码如下

if(condition){
    //hundreds of lines of code
}else if{
    //hundreds of lines of code
} else {
    //hundreds of lines of code
}

如果我有一段时间没有看到它,我无法浏览此代码,因为我经常需要来回滚动以检查我所处的语句的哪个分支或者我是否处于循环中,或者是什么调用循环迭代器。

我的预感是将长行代码放在函数中,并在语句的分支内或循环内调用它们,因此循环和树如果更短,因此更具可读性。我创建的函数是合理的代码 - 孤岛,而不仅仅是随意删除当前的代码。

但是,这是我的问题:这是一个好主意吗?或者,如果在if语句或for循环中有数百行代码,是一种错误的编码风格?我不是一个真正有经验的程序员,但我很欣赏干净的代码:)

谢谢!

添加了: 数百行代码在大多数情况下都不重复。我理解并尝试坚持DRY原则。

14 个答案:

答案 0 :(得分:11)

能够在一个屏幕上看到整个方法通常是一个好主意。如果你必须向上和向下滚动太多(有些人会争辩),那么你就有可能丢失信息。

总的来说,你提出的建议是个好主意。虽然您不需要将所有代码放在一个分支中的一个方法中。可能会有重复的代码段,因此您可以按如下方式对其进行分解:

if(condition){
    CommonMethod();
    SpecificMethodA();
}else if{
    CommonMethod();
    SpecificMethodB();
} else {
    CommonMethod();
    SpecificMethodC();
}

作为一个例子。虽然重构的确切性质完全取决于您的代码。

答案 1 :(得分:5)

根据你的说法,我会将这些块的内容重构为单独的过程(根据需要为它们提供有用的名称和传递参数)。

if(condition){
    DoA();
}else if{
    DoB();
} else {
    DoC();
}

procedure DoA() {
    //hundreds of lines of code
}

procedure DoB() {
    //hundreds of lines of code
}

procedure DoC() {
    //hundreds of lines of code
}

当然,在DoX程序中也可以这样做。

答案 2 :(得分:3)

在单个函数中拥有数百行是一种糟糕的编码风格。具有小功能,希望适合单个屏幕并且只用于一个目的,以便您的代码看起来像

if (condition) {
    func1(); //One purpose
    func2(); //Another purpose
    func3();
} else if (another condition) {
    func4();
} else {
    func5();
    func6();
    func3(); //wow I can reuse some code!
}

不要简单地将每个案例的数百行放在一个函数中。尝试隔离目的(例如,此函数创建一个临时文件)并为它们分配函数。您可能会惊讶地发现您的总行数会下降,因为这些行中很可能会出现重复的功能。

答案 3 :(得分:2)

记住这一点:您 - 或其他阅读代码的人 - 在阅读时应轻松理解每个功能。如果该函数包含数百行代码,那么它将永远不易理解。如果您在阅读时发现自己做笔记以了解代码的作用,则需要重构。

您展示的示例肯定是应该重构的那种。理想情况下,if子句的每个块应该只调用一个函数。

if (condition) 
{ 
    doSomething(); 
} 
else if (anotherCondition) 
{   
    doThisOtherThing();
}
else
{   
    doThisThirdThing();
}

您看到代码是多么容易理解?实际上没有什么可以理解的 - 除了条件之外的条件,但如果这很复杂,它也应该作为一个单独的函数来考虑。

现在,doSomething(),doThisOtherThing()和doThisThirdThing()函数不应该是hundres代码行的普通块。任何功能都不应该那么久。总是很难理解长期的功能。尝试查找函数的逻辑部分,这些部分可以作为单独的函数进行分解,并且可以这样做。如果在所有情况下都应该执行公共部分,则应创建一个公共函数并从所有doX函数调用。

注意:您不需要代码重用来创建函数。您可以在新函数中分解一些内容,以使代码更具可读性。

祝你好运重构! :-)

答案 4 :(得分:1)

除了其他答案:如果您想了解更多相关信息,请尝试阅读Robert C. Martin撰写的“清洁代码”。

答案 5 :(得分:1)

在我看来,作为一个长期编码和代码审查员,答案(像往常一样)有时候。以下是我将代码拆分为单独函数的原因:

  1. 如果分配了函数名,代码将更容易理解。我发现注释仍然很少,创建一个函数名来描述代码块是记录代码块行为的一种方法。
  2. 如果不是那么长,那么父函数(如你所注意的那样)会更易于管理。
  3. 如果可以智能地设计该功能,它可能在其他地方有用,甚至可以帮助您将现有代码重新分解为少量代码。
  4. 但也有理由将代码保留在初始函数中:

    1. 如果内部代码与父函数代码高度交互,那么在子函数之间来回导航可能会更加痛苦。
    2. 如果内部代码与父函数代码高度交互,则必须传递许多参数以维护子函数需要处理的状态,代码的可读性和可维护性较差。
    3. 如果内部代码相对重复且简单,则代码可能首先不可读或无法管理。但请记住,如果它重复且简单,那么它也可能因其他原因而成为重新分解的良好候选者。

答案 6 :(得分:1)

这不是一个好的编码风格 将代码分解为更小的过程使其更具可读性和可操作性。

说完了。不要立即开始将它们切割成较小的程序 只是改变你正在阅读的内容,或者你需要更新的内容。

请记住,每次更改代码时都需要测试更改,因此不要随意更改,因为每次更改代码时都有可能引入新的错误。

答案 7 :(得分:1)

我曾经在一个项目中工作,每个功能的官方限制为20行代码。真是地狱!

我可能正在使用蒙版,将数据移入或移出GUI元素,然后我将被迫编写

transferFirst10Elements()
transferElements11Thru20()
transferElements21Thru30()
transferElements31Thru40()
并且反对我们的政策,作为教条走得太远的一个例子。

只是为了澄清,我认为初始功能确实应该被打破,但我不同意其他一些答复者说故障应该继续,直到没有比屏幕更大的片段。有一点,管理数以万计的微小功能比管理更少的大功能更痛苦。

答案 8 :(得分:1)

每当您重新访问代码时遇到问题这一事实足以表明存在问题。

与此处的大多数其他建议相反,我采取略微不同的方法。

尝试遵循一些关于拆分的位置和数量的建议规则可能有效,但它也可能导致故障本身无法管理的情况。您可以直接浏览分散在源文件中的一些较小的例程,而不是向上和向下滚动以再次熟悉自己。

,而不是...

我建议您尝试专注于将代码拆分为多种方法,其中每种方法执行一项任务,该任务由方法名称明确指出。像这样的代码块通常很大,因为它们做了很多事情。通过以这种方式拆分它们,您会发现您的代码将自然地演变为更易于管理的风格,并且风险更小。

它也可能有助于向另一个方向发展。拿一个较大的代码块,尝试(忽略实际代码)在许多更简单的步骤中定义它的作用,然后相应地编码。记住,它必须做一件事;但可能需要多个步骤才能完成工作。这将提供大型代码块的自然分解。

注意:我绝不会主张对执行单个任务的例程施加行限制;例如,“填充编辑控件”的例程可能有很多行,因为控件数量很多,但是有些人犯的错误是除了填充控件之外还要进行“简单计算”。

这种方法的另一个好处是,您更有可能拥有可以重复使用的例程,因为它们没有“超重行李”。

答案 9 :(得分:0)

过程/函数用于替换重复代码,您只需要更改次要细节/值。这减少了冗余和代码的维护。但是,是的,你也可以使用它(有意义的名字)来减少这么大的代码块。

答案 10 :(得分:0)

您应该考虑将这些块分解为类。 OOP为您提供所需的所有技术。例如,Template method pattern解决了特定于条件的操作中存在CommonAction的问题。这只是OOP冰山的一角。

答案 11 :(得分:0)

我已经通过archimed提出了模板/模式建议(抱歉,我会投票给你,但我的代表显然不是很高)。我自己也在想一个战略模式,但模板也可以解决问题。

我真的很惊讶它被投了票。完成了两种方式(通过将代码移动到方法而不是实现模式),我发现为这些代码块实现模式的工作量确实没那么大。正如ya23所说,实施模式的长期好处是相当可观的。

答案 12 :(得分:0)

您在“if”块中提取长代码片段的想法是在正确的轨道上;但是,我会比那更小。寻找重复代码的较小部分 - 我会说3-10行 - 做一些非常容易理解的事情,并将其提取到一个方法(应该很容易命名)。如果可能,使用自动重构工具(以避免错误)提取并为提取的方法添加单元测试。现在,重复这几次,你应该看到模式和结构开始出现。您最终可能会得到类似于之前答案的结构:

if (condition) {
    func1(); 
    func2(); 
    func3();
} else if (another condition) {
    func4();
} else {
    func5();
    func6();
    func3();
}

答案 13 :(得分:0)

Robert C. Martin 的“清洁代码”说:

<块引用>

if 语句、else 语句、while 语句和 等应该是一行长。可能那行应该是一个函数调用。这不仅使封闭函数保持较小,而且还增加了文档价值,因为在块内调用的函数可以有一个很好的描述性名称。 这也意味着函数不应大到足以容纳嵌套结构。因此,函数的缩进级别不应大于一两个。当然,这会使函数更易于阅读和理解。