Возвращение рано из функции более элегантно, чем оператор if? - PullRequest
16 голосов
/ 10 декабря 2008

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

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

        }

Ответы [ 14 ]

40 голосов
/ 10 декабря 2008

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

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

Минимальное использование Control

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

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

17 голосов
/ 10 декабря 2008

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

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

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

11 голосов
/ 10 декабря 2008

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

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

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

4 голосов
/ 10 декабря 2008

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

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

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

1 голос
/ 10 декабря 2008

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

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

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

1 голос
/ 10 декабря 2008

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

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

0 голосов
/ 17 января 2014

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

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

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

0 голосов
/ 11 декабря 2008

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

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 голосов
/ 10 декабря 2008

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

0 голосов
/ 10 декабря 2008

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

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

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

...