Возвращение рано из функции более элегантно, чем оператор if?

У меня и у коллеги есть спор о том, что из следующего более изящно. Я не буду говорить, кто есть кто, так что это беспристрастно. Что изящнее?

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);

        }
10.12.2008 10:54:21
Это дубликат. Пожалуйста, проверьте это сообщение: stackoverflow.com/questions/36707/…
bruno conde 10.12.2008 11:05:49
вопрос не является дубликатом, хотя да, ответы на мой вопрос также, кажется, отвечают на мой вопрос
Iain 10.12.2008 11:09:08
14 ОТВЕТОВ
РЕШЕНИЕ

Это один из тех случаев, когда можно нарушать правила (т.е. лучшие практики). В общем, вы хотите, чтобы в функции было как можно меньше точек возврата. Практическая причина этого заключается в том, что это упрощает чтение кода, поскольку вы можете просто всегда предполагать, что каждая функция будет принимать свои аргументы, выполнять свою логику и возвращать свой результат. Добавление дополнительных возвратов для различных случаев приводит к усложнению логики и увеличению количества времени, необходимого для чтения и полного получения кода. Как только ваш код достигает стадии обслуживания, множественные возвраты могут оказать огромное влияние на производительность новых программистов, поскольку они пытаются расшифровать логику (это особенно плохо, когда комментарии редки, а код неясен). Задача растет экспоненциально по отношению к длине функции.

Так почему же в этом случае все предпочитают вариант 2? Это потому, что вы настраиваете контракт, который функция применяет путем проверки входящих данных или других инвариантов, которые, возможно, потребуется проверить. Самым красивым синтаксисом для создания проверки является проверка каждого условия, возвращаемая немедленно, если условие не соответствует действительности. Таким образом, вам не нужно поддерживать какое-то логическое значение isValid во всех ваших проверках.

Подводя итог: мы действительно смотрим, как написать проверочный код, а не общую логику; вариант 2 лучше для кода проверки.

19
10.12.2008 12:11:07
Первый абзац этого ответа должен быть написан жирным шрифтом, так как факты там настолько правдивы и не должны игнорироваться.
ViRuSTriNiTy 15.01.2020 10:12:36

В большинстве случаев ранний возврат уменьшает сложность и делает код более читабельным.

Это также один из методов, применяемых в спартанском программировании :

Минимальное использование контроля

  1. Минимизация использования условных выражений с помощью специализированных конструкций, таких как тернаризация, наследование и классы, такие как Class Defaults, Class Once и Class Separator
  2. Упрощение условного с ранним return.
  3. Минимизация использования циклических конструкций с помощью классов-аппликаторов действий, таких как Class Separate и Class FileSystemVisitor.
  4. Упрощая логику итерации с ранними выходами ( с помощью return, continue и breakзаявлением).

В вашем примере я бы выбрал вариант 2, так как он делает код более читабельным. Я использую ту же технику при проверке параметров функции.

41
8.06.2009 09:40:49
Просматривая ссылку на спартанское программирование, я должен сказать, что мне не совсем нравится то, что я вижу. Почти нет пробелов? Короткие имена переменных? Для петель без фигурных скобок? хм ...
Iain 10.12.2008 14:04:22
Я согласен, Лейн. я не читал его, но: без пробелов, коротких имен, циклов без фигурных скобок - все, что мне не нравится. iwwwww
Johannes Schaub - litb 11.12.2008 01:09:02

В этом случае (один тест, никакое другое предложение) мне нравится тест-и-возврат. Это дает понять, что в этом случае ничего не нужно делать, не читая остальную часть функции.

Тем не менее, это расщепление лучших волосков. Я уверен, что у вас должны быть большие проблемы, чтобы беспокоиться :)

0
10.12.2008 11:01:04

Ах, хранитель.

Имхо, да - логика этого более ясна, потому что возвращение является явным и находится рядом с условием, и его можно сгруппировать с подобными структурами. Это еще более применимо, когда «return» заменяется «throw new Exception».

1
10.12.2008 11:03:38
извините за невежество - что такое "страж"?
Iain 10.12.2008 11:06:24
шаблон, который вы описываете в методе 2 (возвращение рано), часто называют «опекуном» или «привратником», потому что он блокирует доступ к остальной части метода - вам нужно пройти через несколько опекунов, прежде чем делать что-либо
annakata 10.12.2008 12:03:37

Вариант 2 более читабелен, но управляемость кода не работает, когда может потребоваться добавить еще.

Так что, если вы уверены, что вариант 2 больше не подходит, но если есть возможность для условия else, я бы предпочел вариант 1

0
10.12.2008 11:05:09

Пока ранние возвраты организованы в виде блока в верхней части тела функции / метода, я думаю, что они гораздо более читабельны, чем добавление еще одного уровня вложенности.

Я стараюсь избегать ранних возвратов в середину тела. Иногда это лучший способ, но в большинстве случаев я думаю, что они усложняют.

Также, как правило, я стараюсь свести к минимуму структуры контроля вложенности. Очевидно, вы можете зайти слишком далеко, так что вам нужно проявить осторожность. Преобразование вложенных if в один переключатель / регистр намного понятнее для меня, даже если предикаты повторяют некоторые подвыражения (и предполагается, что это не критичный к производительности цикл в языке, слишком глупом для устранения подвыражений). Особенно мне не нравится комбинация вложенных if в длинных телах функций / методов, поскольку, если по какой-то причине вы попадаете в середину кода, вы в конечном итоге прокручиваете вверх и вниз, чтобы мысленно восстановить контекст данной строки.

11
10.12.2008 11:09:49

Вариант 1 лучше, потому что вы должны иметь минимальное количество точек возврата в процедуре. Есть исключения как

  если) {
    возврат х;
  }
  вернуть у;

из-за того, как работает язык, но в целом лучше иметь как можно меньше точек выхода.

0
10.12.2008 11:29:25
Почему? =) В примере от asker наличие нескольких точек возврата помогает прояснить код.
gnud 10.12.2008 11:52:58
Ну, бывают моменты, когда вы думаете: «Почему я сделал так много возвратов, теперь я должен закрыть свое соединение рядом с каждым из них», но это просто означает, что ваш код очень плохо спроектирован.
bezmax 10.12.2008 12:01:32

Как было сказано ранее, ранний возврат более читабелен, особенно если тело функции длинное, вы можете обнаружить, что удаление} по ошибке в трехстраничной функции (что само по себе не очень элегантно) и попытка ее компиляции может занять несколько минут неавтоматизированной отладки.

Это также делает код более декларативным, потому что так вы бы описали его другому человеку, поэтому, вероятно, разработчик достаточно близок к тому, чтобы понять его.

Если сложность функции увеличивается позже, и у вас есть хорошие тесты, вы можете просто обернуть каждую альтернативу в новую функцию и вызывать их в случае ветвей, таким образом, вы поддерживаете декларативный стиль.

1
10.12.2008 11:40:17

По моему опыту, проблема с использованием ранней отдачи в проекте заключается в том, что если другие участники проекта не привыкли к ним, они не будут их искать. Так рано возвращаться или нет - если задействовано несколько программистов, убедитесь, что все, по крайней мере, знают об их присутствии.

Я лично пишу код для возврата, как только это возможно, поскольку задержка возврата часто вносит дополнительную сложность, например, пытается безопасно выйти из группы вложенных циклов и условий.

Поэтому, когда я смотрю на незнакомую функцию, самое первое, что я делаю, это ищу все returns. Что действительно помогает, так это настроить цвет синтаксиса так, чтобы returnон отличался от всего остального. (Я обращаюсь к красному.) Таким образом, returns становятся полезным инструментом для определения того, что делает функция, а не скрытым камнем преткновения для неосторожных.

4
10.12.2008 11:48:52

Я предпочитаю избегать немедленного возврата в начале функции и, по возможности, помещать соответствующую логику, чтобы предотвратить вход в метод до его вызова. Конечно, это варьируется в зависимости от цели метода.

Тем не менее, я не против вернуться к середине метода, при условии, что метод короткий и читаемый. В случае, если метод большой, по моему мнению, он уже не очень читабелен, поэтому он либо будет реорганизован в несколько функций со встроенными возвратами, либо я явно откажусь от структуры управления с единственным возвратом в конце.

0
10.12.2008 12:04:12

У меня возникает соблазн закрыть его как точную копию, так как я уже видел несколько похожих потоков, включая оператор Invert «if» для уменьшения вложенности, который имеет хорошие ответы.

Я позволю этому жить пока ... ^ _ ^

Чтобы ответить на этот вопрос, я верю, что раннее возвращение в качестве охранной оговорки лучше, чем глубоко вложенные ifs.

0
23.05.2017 12:32:08

Я видел оба типа кодов, и я предпочитаю первый, так как он выглядит легко читаемым и понятным для меня, но я читал много мест, которые существуют на ранних стадиях - лучший путь.

0
10.12.2008 13:06:09

Есть по крайней мере еще одна альтернатива. Отделите детали фактической работы от решения о том, выполнять ли эту работу. Что-то вроде следующего:

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);

    }

Любой из этих трех (ваши два плюс третий выше) являются разумными для таких маленьких случаев, как этот. Однако было бы плохо иметь функцию длиной в сотни строк с разбросанными по ней несколькими «точками выхода».

0
11.12.2008 01:03:22

У меня были эти дебаты с моим собственным кодом на протяжении многих лет. Я начал жизнь в пользу одного возвращения и медленно провалился.

В этом случае я предпочитаю вариант 2 (один возврат) просто потому, что мы говорим только о 7 строках кода, заключенных в if () без какой-либо другой сложности. Это гораздо более читабельным и функциональным. Это течет сверху вниз. Вы знаете, что начинаете сверху и заканчиваете снизу.

При этом, как уже говорили другие, если в начале было больше охранников или больше сложностей или если функция росла, то я бы предпочел вариант 1: немедленно вернуться в начало для простой проверки.

0
16.01.2014 21:20:02