Несмотря на то, что он довольно короткий, самый популярный раздел Java для небольших команд (судя по количеству комментариев на Reddit) - это раздел плохой совет в конце.

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

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

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

Краткое изложение правила единой точки выхода

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

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

Возможно.

Если ваш метод состоит из 100 строк, вероятно, полезно знать, что он может выйти только в конце. Но мы больше не пишем 100-строчные методы.

Do we?

Что произойдет, если вы скажете программисту на Java, что все методы должны иметь только один оператор возврата?

Вероятно, они напишут что-нибудь в этом роде.

Не так уж плохо, правда?

Как насчет того, чтобы мы не дали им указание иметь единую точку выхода?

Вы, вероятно, получите что-то подобное.

Что с этим не так?

Ничего такого.

На самом деле это немного яснее, чем метод единственной точки выхода.

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

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

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

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

Теперь это лучше, чем версия с несколькими выходами?

No.

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

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

Мартин Фаулер и Кент Бек красиво изложили эту тему в статье «Рефакторинг: улучшение дизайна существующего кода»

«… Одна точка выхода - действительно бесполезное правило. Ясность - ключевой принцип: если метод более ясен с одной точкой выхода, используйте одну точку выхода; иначе не надо ».

Отзыв сообщества

Оказывается, есть еще несколько сторонников правила единой точки выхода, и мы получили хорошие отзывы от Жана-Батиста Жирудо, который работает над проектами Functional Java и derive4j.

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

Это правда, и это побудило меня задуматься, почему это хорошо работает в таких ситуациях, но не для Java.

Ответ, конечно, в том, что это разные языки.

Функциональные языки основаны на выражениях (т.е. все возвращает значение), тогда как код Java состоит в основном из операторов.

Чтобы продемонстрировать разницу, давайте посмотрим на код Scala. Поскольку в Scala все является выражением, допустимо следующее.

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

Следовательно, версия нашего примера функции на Scala может выглядеть так:

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

Конечно, Java состоит не только из операторов, но и из некоторых выражений.

Это подводит нас к этой интересной реализации с единственной точкой выхода, которую я не рассматривал.

При этом используется оператор Java ?, который формирует выражение точно так же, как if в Scala, возвращая значение с любой стороны ветви.

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

Это явно превосходит нашу более раннюю версию с несколькими выходами?

No.

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

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

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