21

我和一位同事对以下哪个更优雅存在争议。我不会说谁是谁,所以是不偏不倚的。哪个更优雅?

public function set hitZone(target:DisplayObject):void
        {
            if(_hitZone != target)
            {
                _hitZone.removeEventListener(MouseEvent.ROLL_OVER, onBtOver);
                _hitZone.removeEventListener(MouseEvent.ROLL_OUT, onBtOut);
                _hitZone.removeEventListener(MouseEvent.MOUSE_DOWN, onBtDown);

                _hitZone = target;

                _hitZone.addEventListener(MouseEvent.ROLL_OVER, onBtOver, false, 0, true);
                _hitZone.addEventListener(MouseEvent.ROLL_OUT, onBtOut, false, 0, true);
                _hitZone.addEventListener(MouseEvent.MOUSE_DOWN, onBtDown, false, 0, true);
            }
        }

...或者...

public function set hitZone(target:DisplayObject):void
        {
            if(_hitZone == target)return;

            _hitZone.removeEventListener(MouseEvent.ROLL_OVER, onBtOver);
            _hitZone.removeEventListener(MouseEvent.ROLL_OUT, onBtOut);
            _hitZone.removeEventListener(MouseEvent.MOUSE_DOWN, onBtDown);

            _hitZone = target;

            _hitZone.addEventListener(MouseEvent.ROLL_OVER, onBtOver, false, 0, true);
            _hitZone.addEventListener(MouseEvent.ROLL_OUT, onBtOut, false, 0, true);
            _hitZone.addEventListener(MouseEvent.MOUSE_DOWN, onBtDown, false, 0, true);

        }
4

14 回答 14

44

在大多数情况下,提前返回会降低复杂性并使代码更具可读性。

这也是斯巴达编程中应用的技术之一:

最少使用控制

  1. 通过使用诸如三元化、继承和类(如 Class Defaults、Class Once 和 Class Separator)之类的专门构造来最小化条件句的使用
  2. 用 early 简化条件return
  3. 通过使用动作应用程序类(例如 Class Separate 和 Class FileSystemVisitor),最大限度地减少循环结构的使用。
  4. 通过提前退出(通过 和 语句)简化迭代return逻辑 。continuebreak

在您的示例中,我会选择选项 2,因为它使代码更具可读性。检查函数参数时,我使用相同的技术。

于 2008-12-10T10:58:54.023 回答
21

这是可以打破规则(即最佳实践)的情况之一。通常,您希望函数中的返回点尽可能少。这样做的实际原因是它简化了您对代码的阅读,因为您总是可以假设每个函数都会接受它的参数,执行它的逻辑并返回它的结果。为各种情况添加额外的回报往往会使逻辑复杂化,并增加阅读和完全理解代码所需的时间。一旦您的代码进入维护阶段,那么多次返回可能会对新程序员的生产力产生巨大影响,因为他们试图破译逻辑(当注释稀疏且代码不清楚时尤其糟糕)。问题随着函数的长度呈指数增长。

那么为什么在这种情况下每个人都更喜欢选项 2?这是因为您正在设置一个合约,该函数通过验证传入数据或其他可能需要检查的不变量来强制执行。构造验证的最漂亮的语法是检查每个条件,如果条件无效,则立即返回。这样您就不必通过所有检查来维护某种 isValid 布尔值。

总结一下:我们真正关注的是如何编写验证代码而不是一般逻辑;选项 2 更适合验证代码。

于 2008-12-10T12:11:07.137 回答
11

只要将早期返回组织为函数/方法体顶部的块,那么我认为它们比添加另一层嵌套更具可读性。

我尽量避免在身体中间提前返回。有时它们是最好的方法,但大多数时候我认为它们很复杂。

此外,作为一般规则,我尽量减少嵌套控制结构。显然你可以把这个做得太过分了,所以你必须谨慎行事。将嵌套的 if 转换为单个 switch/case 对我来说要清楚得多,即使谓词重复一些子表达式(并且假设这不是一种语言中的性能关键循环,因为它太愚蠢而无法消除子表达式)。特别是我不喜欢长函数/方法体中嵌套 if 的组合,因为如果你出于某种原因跳到代码中间,你最终会上下滚动以在心理上重建给定行的上下文。

于 2008-12-10T11:09:49.837 回答
4

根据我的经验,在项目中使用早期回报的问题在于,如果项目中的其他人不习惯它们,他们就不会寻找它们。无论是否提前返回 - 如果涉及多个程序员,请确保每个人至少都知道他们的存在。

我个人编写代码以尽快返回,因为延迟返回通常会引入额外的复杂性,例如尝试安全地退出一堆嵌套循环和条件。

所以当我看到一个不熟悉的函数时,我做的第一件事就是寻找所有的returns。真正有帮助的是设置您的语法着色以提供return与其他任何东西不同的颜色。(我喜欢红色。)这样,returns 成为确定函数作用的有用工具,而不是为粗心的人隐藏绊脚石。

于 2008-12-10T11:48:52.330 回答
1

啊守护者。

恕我直言,是的 - 它的逻辑更清晰,因为返回是明确的并且紧邻条件,并且可以很好地与相似的结构分组。这更适用于将“return”替换为“throw new Exception”的情况。

于 2008-12-10T11:03:38.133 回答
1

如前所述,提前返回更具可读性,特别是如果函数体很长,您可能会发现在 3 页函数中错误地删除了 }(这本身不是很优雅)并尝试编译它可能需要几分钟的非自动化调试。

它还使代码更具声明性,因为这是您向另一个人描述它的方式,因此开发人员可能足够接近可以理解它。

如果稍后函数的复杂性增加,并且您有良好的测试,您可以简单地将每个替代方案包装在一个新函数中,并在分支分支中调用它们,这样您就可以保持声明式风格。

于 2008-12-10T11:40:17.433 回答
0

在这种情况下(一个测试,没有其他子句)我喜欢测试和返回。它清楚地表明,在这种情况下,无需阅读函数的其余部分,无需执行任何操作。

然而,这正在分裂最好的头发。我相信你一定有更大的问题要担心:)

于 2008-12-10T11:01:04.220 回答
0

选项 2 更具可读性,但是当可能需要添加 else 时,代码的可管理性会失败。

因此,如果您确定,选项 2 没有其他选择,但如果可能有其他条件的范围,那么我更喜欢选项 1

于 2008-12-10T11:05:09.533 回答
0

选项 1 更好,因为您应该在程序中有最少数量的返回点。有例外,例如

  如果一个) {
    返回 x;
  }
  返回 y;

由于语言的工作方式,但总的来说,最好尽可能少地设置退出点。

于 2008-12-10T11:29:25.630 回答
0

我更喜欢避免在函数开头立即返回,并尽可能放置限定逻辑以防止在调用之前进入该方法。当然,这取决于方法的目的。

但是,我不介意在方法中间返回,只要方法简短易读。如果方法很大,在我看来,它已经不是很可读了,所以它要么被重构为具有内联返回的多个函数,要么我将显式地从控制结构中分离出来,最后只返回一个返回。

于 2008-12-10T12:04:12.207 回答
0

我很想将其关闭为完全重复,因为我已经看到了一些类似的线程,包括反转“if”语句以减少嵌套,这有很好的答案。

我现在就让它活下去……^_^

为了回答这个问题,我相信作为保护子句的早期返回比深度嵌套的 if 更好。

于 2008-12-10T12:43:31.597 回答
0

我已经看过这两种类型的代码,我更喜欢第一种,因为它看起来很容易阅读和理解,但我读过很多早期存在的地方是更好的方法。

于 2008-12-10T13:06:09.290 回答
0

至少还有另一种选择。将实际工作的细节与是否执行工作的决定分开。类似于以下内容:

public function setHitZone(target:DisplayObject):void
    {
        if(_hitZone != target)
            setHitZoneUnconditionally(target);

    }

public function setHitZoneUnconditionally(target:DisplayObject):void
    {
        _hitZone.removeEventListener(MouseEvent.ROLL_OVER, onBtOver);
        _hitZone.removeEventListener(MouseEvent.ROLL_OUT, onBtOut);
        _hitZone.removeEventListener(MouseEvent.MOUSE_DOWN, onBtDown);

        _hitZone = target;

        _hitZone.addEventListener(MouseEvent.ROLL_OVER, onBtOver, false, 0, true);
        _hitZone.addEventListener(MouseEvent.ROLL_OUT, onBtOut, false, 0, true);
        _hitZone.addEventListener(MouseEvent.MOUSE_DOWN, onBtDown, false, 0, true);

    }

这三个(你的两个加上上面的第三个)中的任何一个对于像这样小的案件都是合理的。然而,拥有数百行长的函数并在其中散布多个“救助点”将是一件坏事。

于 2008-12-11T01:03:22.557 回答
0

多年来,我一直在用我自己的代码进行辩论。我开始有利于一次回报的生活,然后慢慢地消失了。

在这种情况下,我更喜欢选项 2(一次返回),因为我们只讨论了由 if() 包装的 7 行代码,没有其他复杂性。它更具可读性和功能性。它从上到下流动。你知道你从顶部开始,在底部结束。

话虽如此,正如其他人所说,如果一开始有更多的警卫或更复杂的情况,或者如果函数增长,那么我更喜欢选项 1:在开始时立即返回以进行简单的验证。

于 2014-01-16T21:20:02.530 回答