Является ли ранний возврат из функции более элегантным, чем оператор if?

StackOverflow https://stackoverflow.com/questions/355670

  •  21-08-2019
  •  | 
  •  

Вопрос

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

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

        }
Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

Ах страж.

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

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

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

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

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

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

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

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

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

  if (a) {
    return x;
  }
  return y;

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

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

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

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

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

Чтобы дать на это ответ, я верю, что предложение early return как guard лучше, чем глубоко вложенные ifs.

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

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

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

    }

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top