Возвращается раньше из функции, более элегантной, чем инструкция 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);

        }

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

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

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

Ответ 10

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

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

Ответ 11

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

Я позволю ему жить сейчас... ^ _ ^

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

Ответ 12

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

Ответ 13

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

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

    }

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

Ответ 14

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

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

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