当前位置:  开发笔记 > 编程语言 > 正文

一个函数应该只有一个return语句吗?

如何解决《一个函数应该只有一个return语句吗?》经验,为你挑选了28个好方法。

是否有充分的理由说明为什么在函数中只有一个return语句是更好的做法?

或者,一旦逻辑正确就可以从函数返回,这意味着函数中可能有很多返回语句?



1> Matt Hamilto..:

我经常在一个方法的开头有几个语句来返回"简单"的情况.例如,这个:

public void DoStuff(Foo foo)
{
    if (foo != null)
    {
        ...
    }
}

......可以像这样更具可读性(恕我直言):

public void DoStuff(Foo foo)
{
    if (foo == null) return;

    ...
}

所以,是的,我认为从函数/方法中获得多个"退出点"是很好的.


这被称为"守卫声明"是福勒的重构.
同意.虽然有多个退出点可能会失控,但我肯定认为它比将整个函数放在IF块中更好.尽可能经常使用return来保持代码的可读性.
巨大的if-else语句块,每个都带有返回值?那没什么.这样的事情通常很容易被重构.(至少结果变量更常见的单一退出变化是,所以我怀疑多退出变化会更难.)如果你真的很头疼,看看if-else和while循环的组合(由本地控制) booleans),其中设置了结果变量,导致方法结束时的最终退出点.这就是疯狂的单一退出想法,是的,我正在谈论必须处理它的实际经验.
当函数保持相对较短时,不难遵循具有靠近中间的返回点的函数的结构.
'想象一下:你需要在'DoStuff'函数结束时执行"IncreaseStuffCallCounter"方法.在这种情况下你会做什么?:)' - `DoStuff(){DoStuffInner(); IncreaseStuffCallCounter(); }`
在我已经在深度嵌套中放置返回的情况下(ug!)我尝试在代码中非常大声地评论它,以便下一个维护者看到我做了什么.
我是前后条件的所有人,但这就是我的责任.最糟糕的是当你遇到一大块if-else语句时,每个语句都有一个返回.这种代码是改变和测试的噩梦.
当然,在大多数情况下,如果参数为"null"而您期望某个值,则最好抛出异常.有一个可以采用`null`*的参数可以*有用,但它应该谨慎使用恕我直言.当然例外的是退出点作为return语句,并且当你指向方法的返回类型时,大多数IDE现在突出显示所有退出点.
在函数开头有一组检查/返回或聚集到一个部分是合理使用多个return语句.通常,问题是当开发人员在嵌套的if-else和循环内的整个函数中散布返回语句时.
想象一下:你需要在'DoStuff'函数结束时执行"IncreaseStuffCallCounter"方法.在这种情况下你会做什么?:)有独特的回报和享受.如果你的方法太复杂,只有一个回报 - 拆分它.
@LarsWestergren卫兵甚至在伟大的Martin Fowler之前约会:)

2> Chris S..:

没有人提到或引用Code Complete,所以我会这样做.

17.1回归

最小化每个例程中的返回数.如果从底部读取它,你就不会意识到它返回到某个地方的可能性.

在增强可读性时使用返回.在某些例程中,一旦知道答案,就要立即将其返回到调用例程.如果以不需要任何清理的方式定义例程,则不立即返回意味着您必须编写更多代码.


+1为"最小化"但不禁止多次返回的细微差别.
就个人而言,我赞成尽早返回.为什么?好吧,当你看到给定案例的return关键字时,你立即知道"我已经完成了" - 你不必继续阅读以弄清楚之后发生了什么,如果有的话.
"难以理解"是非常主观的,特别是当作者没有提供任何经验证据来支持一般性主张时......在最终退货声明的代码中,必须在许多条件位置设置单个变量同样受到"没有意识到变量被分配到其上面的函数的可能性!
@HestonT.Holtmann:在编程书籍中使_Code Complete_独有的事情是,这些建议得到了经验证据的支持.
这可能是接受的答案,因为它提到有多个返回点并不总是好的,但有时是必要的.
"......如果,在底部阅读,......" - 有关这方面的一些事情.1)谁从底部开始阅读例程?2)如果例程的底部距离顶部很远,以至于无法跟踪返回,那么您的例程应该可以分解为多个例程.3)如果例程具有返回值,那么确保返回变量具有正确的值与跟踪多个返回语句一样多.

3> ljs..:

我会说任意对多个退出点进行任意决定是非常不明智的,因为我发现这种技术在实践中反复使用,实际上为了清楚起见,我经常将现有代码重构为多个退出点.我们可以比较这两种方法: -

string fooBar(string s, int? i) {
  string ret = "";
  if(!string.IsNullOrEmpty(s) && i != null) {
    var res = someFunction(s, i);

    bool passed = true;
    foreach(var r in res) {
      if(!r.Passed) {
        passed = false;
        break;
      }
    }

    if(passed) {
      // Rest of code...
    }
  }

  return ret;
}

与此相比,有多个出口点的代码允许: -

string fooBar(string s, int? i) {
  var ret = "";
  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

我认为后者相当清楚.据我所知,多个退出点的批评现在是一个相当古老的观点.


@Rick 1.根据我的经验,这实际上并不是我曾多次遇到的模式,2.它是在"其余代码"中分配的,也许这一点并不清楚.嗯?这是一个例子吗?好吧,我想这是在这方面做出的,但是有可能证明这一点,5.可以......
清晰度在旁观者的眼中 - 我看一个函数并寻求一个中间和结束的开始.当功能很小时它很好 - 但是当你试图弄清楚为什么某些东西被破坏而"其余的代码"变得非常重要时,你可以花费很多时间寻找为什么ret是
首先,这是一个人为的例子.第二,在哪里:字符串ret;"进入第二个版本?第三,Ret包含没有有用的信息.第四,为什么在一个函数/方法中有如此多的逻辑?第五,为什么不将DidValuesPass(类型res)与RestOfCode()分开子功能?
@Rick的意思是,通常更容易返回,而不是将代码包装在一个巨大的if语句中.根据我的经验,即使进行了适当的重构,它也会出现很多.
没有多个return语句的要点是使调试更容易,这是可读性的第二步.最后一个断点允许您查看退出值,无异常.至于可读性,所示的2个函数的行为不同.如果!r.Passed,则默认返回为空字符串,但"更易读"的更改为返回null.作者误解了之前只有几行后出现了默认值.即使是在琐碎的例子中,很容易有不明确的默认回报,最后一次回报有助于强制执行.

4> 17 of 26..:

我目前正在开发一个代码库,其中有两个人在盲目地订阅"单点退出"理论,我可以告诉你,根据经验,这是一个可怕的可怕做法.它使代码非常难以维护,我会告诉你原因.

有了"单点退出"理论,您不可避免地会遇到如下代码:

function()
{
    HRESULT error = S_OK;

    if(SUCCEEDED(Operation1()))
    {
        if(SUCCEEDED(Operation2()))
        {
            if(SUCCEEDED(Operation3()))
            {
                if(SUCCEEDED(Operation4()))
                {
                }
                else
                {
                    error = OPERATION4FAILED;
                }
            }
            else
            {
                error = OPERATION3FAILED;
            }
        }
        else
        {
            error = OPERATION2FAILED;
        }
    }
    else
    {
        error = OPERATION1FAILED;
    }

    return error;
}

这不仅使代码很难遵循,而且现在稍后说你需要返回并在1到2之间添加一个操作.你必须缩进整个怪异功能,祝你好运你的if/else条件和大括号是否正确匹配.

此方法使代码维护极其困难且容易出错.


您可以将其重构为此,保持其"纯度":if(!SUCCEEDED(Operation1())){} else error = OPERATION1FAILED; if(error!= S_OK){if(SUCCEEDED(Operation2())){} else error = OPERATION2FAILED; } if(error!= S_OK){if(SUCCEEDED(Operation3())){} else error = OPERATION3FAILED; } //等
@Murph:我看过这种代码被使用,滥用和过度使用.这个例子非常简单,因为里面没有真正的if/else.所有这些代码需要分解的都是一个"别的"被遗忘的.AFAIK,这段代码真的不需要例外.
此代码不只有一个退出点:每个"error ="语句都在退出路径上.它不仅仅是退出函数,而是退出任何块或序列.
我不同意单一回报"不可避免地"导致深度嵌套.你的例子可以写成一个简单的线性函数,只有一个返回(没有gotos).如果您不能或不能完全依赖RAII进行资源管理,那么早期的返回最终会导致泄漏或重复代码.最重要的是,早期回报使得断言条件变得不切实际.
@Murph:你无法知道在每个条件之后没有其他任何事情没有通过阅读每个条件.通常我会说这些主题是主观的,但这显然是错误的.随着每个错误的返回,你就完成了,你确切知道发生了什么.
通过教条化来滥用好主意的好例子!一般来说,单一的回报是一个好主意,而使用GOTO则不然.现在,使这个代码具有单个返回点并且可维护的一种简单方法就是使用GOTO(或异常).这不具讽刺意味吗?
通常,如果您在函数中考虑多个return语句,则可以将函数重写或分解为更小的函数.虽然它更像是指导而非规则,但它通常表明你采取了错误的方法.
但这只是一个糟糕的实现,它不是由单一出口点本身强加的.单个出口点并不会自动暗示深度嵌套,这就是很多开发人员如何处理它的问题.查看各种按合同设计的实现,这些实现通常在历史上通过深度嵌套或多出口解决,但现在往往有更优雅的解决方案.
没有对象或方法调用分离,这正是任何程序的状态最终看起来像.这里的问题不是单个出口点,而是没有任何其他流控制的大量分层if语句.
我认为这是严格的代码,而不是一个糟糕的校长.Joe Pineda的建议允许单个出口点和更好的结构.
不要打败死马,但这个代码很糟糕的原因不同:功能的大小肯定会增加复杂性.它一下子做了太多事情.至少,最内部的`if`语句很有用,因为它可以被反转(`!SUCCEEDED`).但最重要的是,这个函数正在做多个事情,这些结果可以分解:`function doStep1(){int r = ERROR1; if(SUCCESS(step1()))r = doStep2(); 返回r; 成功时,最后一步不可避免地会返回"OK".在面向对象的设计中,甚至可以将其制作成对象......

5> Chris de Vri..:

结构化编程说你应该每个函数只有一个return语句.这是为了限制复杂性.许多人如Martin Fowler认为用多个return语句编写函数更简单.他在他写的经典重构书中提出了这个论点.如果你遵循他的其他建议并编写小函数,这很有效.我同意这种观点,只有严格的结构化编程纯粹主义者遵守每个函数的单个返回语句.


结构化编程没有说什么.一些(但不是全部)将自己描述为结构化编程的拥护者的人说.
"如果你遵循他的其他建议并编写小函数,这种方法很有效." 这是最重要的一点.小功能很少需要很多返回点.
@wnoise +1评论,如此真实.所有"结构化编程"都表示不使用GOTO.

6> blank..:

正如肯特贝克在实施模式中讨论保护条款时所说的那样,使例程有一个单一的入口和出口点......

"这是为了防止在同一例程中进出许多位置时出现混淆.当应用于FORTRAN或汇编语言程序时,它很有意义,这些程序用大量全局数据编写,甚至理解执行哪些语句也很难.用小方法和大多数本地数据,这是不必要的保守."

我发现一个用guard子句编写的函数比一个长嵌套的if then else语句更容易理解.



7> Apocalisp..:

在没有副作用的函数中,没有充分理由获得多个返回值,您应该以函数式编写它们.在具有副作用的方法中,事物更顺序(时间索引),因此您使用return语句作为命令停止执行,以命令式方式编写.

换句话说,如果可能的话,赞成这种风格

return a > 0 ?
  positively(a):
  negatively(a);

在此

if (a > 0)
  return positively(a);
else
  return negatively(a);

如果您发现自己编写了几层嵌套条件,那么可能有一种方法可以使用谓词列表进行重构.如果您发现ifs和elses在语法上相距甚远,您可能希望将其分解为更小的函数.跨越多个屏幕文本的条件块很难阅读.

没有适用于每种语言的硬性规则.像一个返回语句之类的东西不会使你的代码变好.但好的代码往往会让你以这种方式编写你的函数.


+1"如果你发现你的ifs和elses在语法上相差很远,你可能想把它分解成更小的函数."
+1,如果这是一个问题,通常意味着你在一个函数中做了太多.我真的很沮丧,这不是最高投票的答案

8> Pete Kirkham..:

我已经在C++的编码标准中看到了它,这是C语言的遗留问题,好像你没有RAII或其他自动内存管理那么你必须清理每次返回,这或者意味着剪切和粘贴清理或goto(逻辑上与托管语言中的'finally'相同),这两种都被认为是不良形式.如果你的做法是在C++或其他自动内存系统中使用智能指针和集合,那么就没有强有力的理由,它就变成了可读性和更多的判断调用.



9> Joel Coehoor..:

我倾向于认为函数中间的返回语句很糟糕.你可以使用return在函数的顶部构建一些guard子句,当然告诉编译器在函数结束时返回什么没有问题,但是函数中间的返回很容易错过并且可以使功能更难解释.



10> Adrian McCar..:

是否有充分的理由说明为什么在函数中只有一个return语句是更好的做法?

是的,有:

单一出口点是断言后期条件的绝佳场所.

能够在函数末尾的一个返回上放置调试器断点通常很有用.

回报越少意味着复杂性越低.线性代码通常更容易理解.

如果试图将函数简化为单个返回会导致复杂性,那么这就是重构更小,更通用,更容易理解的函数的动机.

如果您使用的是没有析构函数的语言,或者如果您不使用RAII,那么单次返回会减少您必须清理的地点数量.

某些语言需要单个退出点(例如,Pascal和Eiffel).

这个问题通常被认为是多个回报或深度嵌套的if语句之间的错误二分法.几乎总有第三种解决方案非常线性(没有深度嵌套),只有一个出口点.

更新:显然,MISRA指南也促进单一退出.

要明确的是,我并不是说多次退货总是错的.但是考虑到其他方面相同的解决方案,有很多理由可以选择具有单一回报的解决方案.


另一个很好的理由,可能是目前最好的,只有一个返回语句是记录.如果要将记录添加到方法,可以放置一个日志语句来传达方法返回的内容.

11> 小智..:

拥有单一出口点确实在调试提供了一个优势,因为它可以让你在一个函数的末尾设置一个断点,单看什么样的价值实际上是要被退回.


我很久没见过一个调试器,它不允许你在方法的"关闭"上设置一个断点(结束,右大括号,无论你的语言是什么)并且无论在哪里,或者多少都打到了断点,返回statemets在方法中.此外,即使您的函数只有一个返回,这并不意味着您不能通过异常(显式或继承)退出该函数.所以,我认为这不是一个有效的观点.
BRAVO!你是唯一一个提及*目标*原因的**人.这就是我更喜欢单个退出点与多个退出点的原因.如果我的调试器可以在*任何*退出点上设置断点,我可能更喜欢多个退出点.我目前的观点是编码多个出口点的人为了自己的利益而牺牲那些必须在他们的代码上使用调试器的其他人的利益(并且是的,我说的是所有开源代码编写代码的开源贡献者)多个出口点.)
是.我正在将日志记录代码添加到一个在生产中间歇性行为不当的系统中(我无法单步执行).如果前一个编码器使用单出口,那将会更容易.
没错,在调试中它很有帮助.但实际上我在大多数情况下能够在调用函数之后设置断点 - 实际上是相同的结果.(当然,这个位置在调用堆栈中找到.)YMMV.

12> Scott Dorman..:

一般来说,我尝试从函数中只有一个退出点.然而,有时候这样做实际上最终会创建一个比必要的更复杂的函数体,在这种情况下,最好有多个出口点.它必须是基于由此产生的复杂性的"判断调用",但目标应该是尽可能少的出口点而不牺牲复杂性和可理解性.



13> Richard Cord..:

我倾向于单一退出,除非它确实使事情复杂化.我发现在某些情况下,多个存在点可以掩盖其他更重要的设计问题:

public void DoStuff(Foo foo)
{
    if (foo == null) return;
}

看到这段代码后,我会马上问:

'foo'永远是空的吗?

如果是这样,'DoStuff'有多少客户端使用null'foo'调用该函数?

根据这些问题的答案,可能是这样

    检查是没有意义的,因为它永远不是真的(即它应该是一个断言)

    检查很少是真的,所以最好更改那些特定的调用函数,因为他们应该采取其他一些行动.

在上述两种情况下,代码都可以通过断言重新编写,以确保'foo'永远不会为空并且相关的调用者发生了变化.

还有另外两个原因(具体我认为是C++代码),其中多个存在实际上可能产生负面影响.它们是代码大小和编译器优化.

函数出口范围内的非POD C++对象将调用其析构函数.如果有多个返回语句,则可能是范围内有不同的对象,因此要调用的析构函数列表将不同.因此编译器需要为每个return语句生成代码:

void foo (int i, int j) {
  A a;
  if (i > 0) {
     B b;
     return ;   // Call dtor for 'b' followed by 'a'
  }
  if (i == j) {
     C c;
     B b;
     return ;   // Call dtor for 'b', 'c' and then 'a'
  }
  return 'a'    // Call dtor for 'a'
}

如果代码大小是一个问题 - 那么这可能是值得避免的.

另一个问题涉及"命名返回值优化"(又名Copy Elision,ISO C++ '03 12.8/15).C++允许实现跳过调用复制构造函数,如果它可以:

A foo () {
  A a1;
  // do something
  return a1;
}

void bar () {
  A a2 ( foo() );
}

只需按原样执行代码,对象'a1'在'foo'中构造,然后调用其复制构造来构造'a2'.但是,copy elision允许编译器在堆栈的同一位置构造'a1'作为'a2'.因此,当函数返回时,不需要"复制"对象.

多个出口点使编译器在尝试检测到这一点时的工作变得复杂,并且至少对于VC++的相对较新版本,优化不会发生在函数体具有多个返回的情况下.有关更多详细信息,请参阅Visual C++ 2005中的命名返回值优化.


+1并且在列表的底部向下看,我们有**编码实践存在的真正原因** - NRVO.但是,这是微观优化; 和所有的微优化实践一样,可能是由一位50岁的"专家"开始的,他习惯于在300 kHz PDP-8上进行编程,并且不了解清洁和结构化代码的重要性.一般来说,接受Chris S的建议并在有意义的时候使用多个return语句.

14> Ben Lings..:

不,因为我们不再生活在20世纪70年代了.如果你的功能足够长,多次返回是一个问题,那就太长了.

(除了事实上,具有例外的语言中的任何多行函数无论如何都会有多个出口点.)



15> John Channin..:

具有单个退出点可降低Cyclomatic Complexity,因此理论上可以降低在更改代码时将错误引入代码的可能性.然而,实践往往表明需要更务实的方法.因此,我倾向于只有一个退出点,但如果可读性更高,则允许我的代码有几个.


并不是的."if(...)return; ... return"的圈复杂度 与"if(...){...} return;"相同.它们都有两条路径.

16> Jrgns..:

我强迫自己只使用一个return语句,因为它会在某种意义上产生代码气味.让我解释:

function isCorrect($param1, $param2, $param3) {
    $toret = false;
    if ($param1 != $param2) {
        if ($param1 == ($param3 * 2)) {
            if ($param2 == ($param3 / 3)) {
                $toret = true;
            } else {
                $error = 'Error 3';
            }
        } else {
            $error = 'Error 2';
        }
    } else {
        $error = 'Error 1';
    }
    return $toret;
}

(条件是arbritary ......)

条件越多,函数越大,读取的难度就越大.因此,如果你对代码气味很熟悉,你就会意识到这一点,并希望重构代码.两种可能的解决方案是

多次退货

重构为单独的函数

多次退货

function isCorrect($param1, $param2, $param3) {
    if ($param1 == $param2)       { $error = 'Error 1'; return false; }
    if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
    if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
    return true;
}

单独的功能

function isEqual($param1, $param2) {
    return $param1 == $param2;
}

function isDouble($param1, $param2) {
    return $param1 == ($param2 * 2);
}

function isThird($param1, $param2) {
    return $param1 == ($param2 / 3);
}

function isCorrect($param1, $param2, $param3) {
    return !isEqual($param1, $param2)
        && isDouble($param1, $param3)
        && isThird($param2, $param3);
}

当然,它更长,有点乱,但在这种方式重构功能的过程中,我们已经

创建了许多可重用的函数,

使功能更具人性化,并且

功能的重点是价值为何正确.


-1:不好的例子.您已省略错误消息处理.如果不需要,则isCorrect可以表示为return xx && yy && zz; 其中xx,yy和z是isEqual,isDouble和isThird表达式.

17> Rob Cooper..:

我会说你应该有尽可能多的,或任何使代码更清晰(如保护条款).

我个人从未听过/看到任何"最佳实践"说你应该只有一个回复声明.

在大多数情况下,我倾向于根据逻辑路径尽快退出函数(保护子句就是一个很好的例子).



18> Henrik Gusta..:

有一个单一的退出点有很好的说法,就像对不可避免的"箭头"编程有不好的说法一样.

如果在输入验证或资源分配期间使用多个退出点,我会尝试将所有"错误退出"非常明显地放在函数的顶部.

"SSDSLPedia" 的Spartan Programming文章和"Portland Pattern Repository的Wiki" 的单一功能出口点文章都有一些有见地的论据.当然,还有这篇文章需要考虑.

如果你真的想要一个退出点(在任何非异常启用的语言中),例如为了在一个地方释放资源,我发现goto的谨慎应用是好的; 例如,看看这个相当人为的例子(压缩以节省屏幕空间):

int f(int y) {
    int value = -1;
    void *data = NULL;

    if (y < 0)
        goto clean;

    if ((data = malloc(123)) == NULL)
        goto clean;

    /* More code */

    value = 1;
clean:
   free(data);
   return value;
}

就个人而言,我一般不喜欢箭头编程而不喜欢多个退出点,尽管两者在正确应用时都很有用.当然,最好的是将程序结构化为既不需要也不需要.将您的功能分解为多个块通常有帮助:)

虽然在这样做的时候,我发现我最终会得到多个出口点,就像在这个例子中一样,一些较大的函数被分解为几个较小的函数:

int g(int y) {
  value = 0;

  if ((value = g0(y, value)) == -1)
    return -1;

  if ((value = g1(y, value)) == -1)
    return -1;

  return g2(y, value);
}

根据项目或编码指南,大多数锅炉板代码可以用宏代替.作为旁注,以这种方式将其分解使得函数g0,g1,g2非常容易单独测试.

显然,在一个支持OO和异常的语言中,我不会使用像这样的if语句(或者根本不使用if,如果我能用很少的努力侥幸逃脱它),代码就会更加清晰.并且非箭头.大多数非最终回报可能都是例外.

简而言之;

很少有回报比许多回报更好

不止一次回归比巨箭更好,保护条款通常都可以.

在可能的情况下,例外可能/应该取代大多数"保护条款".


http://www.opengroup.org/onlinepubs/009695399/functions/free.html"如果ptr是空指针,则不会发生任何动作."

19> Anthony..:

我相信多次返回通常都很好(在我用C#编写的代码中).单回归风格是C的保留.但你可能不是用C编码.

在所有编程语言中,没有法律只要求一个方法的出口点.有些人坚持这种风格的优越性,有时他们将其提升为"规则"或"法律",但这种信念并没有任何证据或研究支持.

C代码中不止一种返回样式可能是一种坏习惯,其中资源必须被显式解除分配,但Java,C#,Python或JavaScript等语言具有自动垃圾收集和try..finally块(以及usingC#中的块)等构造),这个论点不适用 - 在这些语言中,需要集中的手动资源释放是非常罕见的.

有些情况下,单个返回更易读,而有些情况则不可读.看看它是否减少了代码行数,使逻辑更清晰或减少了大括号和缩进或临时变量的数量.

因此,使用尽可能多的回报以适应您的艺术感受,因为它是布局和可读性问题,而不是技术问题.

我在博客上已经详细讨论了这个问题.



20> Blessed Geek..:

你知道这句谚语 - 美丽在旁观者的眼中.

有些人发誓NetBeans,有些人用IntelliJ IDEA发誓,有些人用Python发誓,有些人用PHP发誓.

在某些商店,如果你坚持这样做,你可能会失去工作:

public void hello()
{
   if (....)
   {
      ....
   }
}

问题在于可见性和可维护性.

我沉迷于使用布尔代数来减少和简化逻辑和状态机的使用.然而,过去有些同事认为我在编码中使用"数学技术"是不合适的,因为它不可见和可维护.这将是一个糟糕的做法.对不起的人,我使用的技术对我来说是非常明显和可维护的 - 因为当我六个月后回到代码时,我会清楚地理解代码,而不是看到一堆众所周知的意大利面条.

嘿伙计(就像以前的客户曾经说过的那样)做你想做的事,只要你知道如何解决它我需要你解决它.

我记得20年前,我的一位同事因雇用今天所谓的敏捷发展战略而被解雇.他有一个细致的增量计划.但他的经理却对他大吼大叫"你无法逐步向用户发布功能!你必须坚持使用瀑布." 他对经理的回应是增量开发会更准确地满足客户的需求.他相信为客户需求开发,但经理相信编码符合"客户的要求".

我们经常因打破数据规范化,MVP和MVC边界而感到内疚.我们内联而不是构建一个函数.我们采取捷径.

就个人而言,我认为PHP是不好的做法,但我知道什么.所有的理论论点都归结为尝试实现一套规则

质量=精确性,可维护性和盈利能力.

所有其他规则都淡入背景.当然,这条规则永远不会消退:

懒惰是优秀程序员的优点.



21> David Clarke..:

我倾向于使用保护条款提前返回,否则在方法结束时退出.单个进入和退出规则具有历史意义,在处理具有多个返回(以及许多缺陷)的单个C++方法的10个A4页面的遗留代码时尤其有用.最近,公认的良好做法是保持方法较小,这使得多个出口对理解的阻抗较小.在下面从上面复制的Kronoz示例中,问题是//其余代码中出现的内容......?:

void string fooBar(string s, int? i) {

  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

我意识到这个例子有点做作,但我很想将foreach循环重构为一个LINQ语句,然后可以将其视为一个保护条款.再次,在一个人为的例子的代码的意图是看不出来和someFunction()可以具有某些其他副作用或结果可能在使用的代码//休息....

if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;

给出以下重构函数:

void string fooBar(string s, int? i) {

  if (string.IsNullOrEmpty(s) || i == null) return null;
  if (someFunction(s, i).Any(r => !r.Passed)) return null;

  // Rest of code...

  return ret;
}



22> OysterD..:

我能想到的一个很好的理由是代码维护:你只有一个退出点.如果你想改变结果的格式,......,它实现起来要简单得多.另外,为了调试,你可以在那里坚持一个断点:)

话虽如此,我曾经不得不在一个库中工作,其中编码标准强加了"每个函数一个返回语句",我发现它非常难.我写了很多数值计算代码,经常有"特殊情况",所以代码最终很难遵循......



23> Jon Limjap..:

多个出口点适用于足够小的功能 - 也就是说,可以在一个屏幕上查看整个功能.如果冗长的功能同样包括多个出口点,则表明该功能可以进一步切断.

这就是说除非绝对必要,否则我会避免多次退出功能.我感到很多错误的痛苦,这些错误是由于在更复杂的功能中某些模糊的线路中的一些偏离返回.



24> Phil Bennett..:

我已经使用了可怕的编码标准,迫使你只有一个退出路径,结果几乎总是非结构化的意大利面,如果这个功能不算什么微不足道的话 - 你最终会有很多休息时间而且会继续阻碍你.



25> ima..:

单一退出点 - 所有其他条件相同 - 使代码更具可读性.但有一个问题:流行建筑

resulttype res;
if if if...
return res;

是假的,"res ="并不比"返回"好多少.它有单个return语句,但是函数实际结束的多个点.

如果你有多个返回函数(或"res ="s),通常最好将它分成几个带有单个退出点的较小函数.



26> Matt Sheppar..:

我通常的策略是在函数末尾只有一个return语句,除非通过添加更多代码来大大减少代码的复杂性.事实上,我更喜欢Eiffel,它通过没有return语句强制执行唯一的返回规则(只有一个自动创建的'result'变量来放置你的结果).

当然有些情况下代码可以通过多次返回比没有它们的明显版本更清晰.有人可能会争辩说,如果你的函数太复杂而没有多个return语句就无法理解,那么就需要更多的返工,但有时候对这些事情务实是好的.



27> Brad Gilbert..:

如果您最终得到的回报超过几个,那么您的代码可能会出现问题.否则我会同意,有时能够从子例程中的多个位置返回是很好的,特别是当它使代码更清晰时.

Perl 6:不好的例子

sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}

会更好地写这样的

Perl 6:很好的例子

@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}

请注意,这只是一个简单的例子



28> Alphaneo..:

我最后投票给单回报作为指导.这有助于常见的代码清理处理 ......例如,看看下面的代码......

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}

推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有