Стиль программирования: необходимо ли возвратиться рано, если защитное условие не удовлетворено?

Одна вещь я иногда задавался вопросом, - который является лучшим стилем из двух, показанных ниже (если таковые имеются)? Лучше возвратиться сразу, если защитное условие не было удовлетворено, или необходимо ли только сделать другой материал, если защитное условие удовлетворено?

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

// Style 1
public SomeType aMethod() {
  SomeType result = null;

  if (!guardCondition()) {
    return result;
  }

  doStuffToResult(result);
  doMoreStuffToResult(result);

  return result;
}

// Style 2
public SomeType aMethod() {
  SomeType result = null;

  if (guardCondition()) {
    doStuffToResult(result);
    doMoreStuffToResult(result);
  }

  return result;
}
30
задан skaffman 11 February 2012 в 15:38
поделиться

10 ответов

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

// Style 3
public SomeType aMethod() {

  if (!guardCondition()) {
    return null;
  }

  SomeType result = new SomeType();
  doStuffToResult(result);
  doMoreStuffToResult(result);

  return result;
}
37
ответ дан 27 November 2019 в 23:06
поделиться

Я обучался структурированному программированию Джексона в конце 80-х, и моя укоренившаяся философия всегда заключалась в том, что «функция должна иметь одну точку входа и одну точку выхода»; это означало, что я написал код в соответствии со стилем 2.

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

Кто сказал, что старые собаки не могут научиться новым трюкам? ;)

27
ответ дан 27 November 2019 в 23:06
поделиться

Стиль 1 - это то, что косвенно рекомендует ядро Linux.

Из http://www.kernel.org/doc/Documentation/CodingStyle, глава 1:

Теперь, некоторые люди будут утверждать, что 8-символьные отступы заставляют код слишком сильно смещается вправо, и его трудно читать на 80-символьном экране терминала. На это можно ответить, что если вам нужно более 3 уровней отступов, вы в любом случае облажались, и должны исправить свою программу.

Стиль 2 добавляет уровни отступов, поэтому он не рекомендуется.

Лично мне нравится стиль 1. Стиль 2 усложняет согласование закрывающих скобок в функциях, имеющих несколько защитных тестов.

14
ответ дан 27 November 2019 в 23:06
поделиться

Я бы сказал: "Это зависит от..."

В ситуациях, когда мне нужно выполнить последовательность очистки более чем из 2-3 строк перед выходом из функции/метода, я бы предпочел стиль 2, потому что последовательность очистки должна быть написана и изменена только один раз. Это означает, что сопровождение будет проще.

Во всех остальных случаях я бы предпочел стиль 1.

0
ответ дан 27 November 2019 в 23:06
поделиться

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

3
ответ дан 27 November 2019 в 23:06
поделиться

Я не знаю, подходит ли здесь охрана . Обычно неудовлетворенный охранник приводит к исключению или утверждению.
Но помимо этого я бы выбрал стиль 1 , потому что он, на мой взгляд, сохраняет код более чистым. У вас есть простой пример с одним условием. Но что происходит со многими условиями и стилем 2? Это приводит к множеству вложенных if или огромных if-условий (с || , && ). Я думаю, что лучше вернуться из метода, как только вы узнаете, что можете.
Но это, конечно, очень субъективно ^^

6
ответ дан 27 November 2019 в 23:06
поделиться

Номер 1 - обычно простой, ленивый и небрежный способ. Номер 2 четко выражает логику. Другие отметили, что да, это может стать громоздким. Однако у этой тенденции есть важное преимущество. Стиль №1 может скрыть, что ваша функция, вероятно, делает слишком много. Это не очень хорошо наглядно демонстрирует сложность происходящего. Т.е. это не позволяет коду сказать вам: «Эй, это становится слишком сложным для этой функции». Это также облегчает другим разработчикам, которые не знают ваш код, пропустить те возвраты, которые разбросаны здесь и там, во всяком случае на первый взгляд.

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

-2
ответ дан 27 November 2019 в 23:06
поделиться

Если вы покопаетесь в .net-Framework с помощью .net-Reflector, то увидите, что программисты .net используют стиль 1 (или, возможно, стиль 3, уже упомянутый unbeli). Причины уже упомянуты в ответах выше. и, возможно, еще одна причина - сделать код более читабельным, кратким и ясным. Больше всего этот стиль используется при проверке входных параметров, вам всегда придется это делать, если вы программируете что-то вроде frawework/library/dll. сначала проверьте все входные параметры, а потом работайте с ними.

5
ответ дан 27 November 2019 в 23:06
поделиться

Иногда это зависит от языка и от того, какие виды "ресурсов" вы используете (например, открытые ручки файлов).

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

В C++, использующем RAII-стиль программирования, оба стиля одинаково безопасны, поэтому вы можете выбрать тот, который удобнее. Лично я использую стиль 1 с C++ в стиле RAII. C++ без RAII похож на C, поэтому, опять же, стиль 2, вероятно, лучше в этом случае.

В таких языках, как Java со сборкой мусора, среда выполнения помогает сгладить различия между двумя стилями, поскольку она сама убирает за собой мусор. Однако и в этих языках могут возникнуть тонкие проблемы, если вы явно не "закрываете" некоторые типы объектов. Например, если вы создадите новый java.io.FileOutputStream и не закроете его перед возвратом, то связанный с ним хэндл операционной системы будет оставаться открытым до тех пор, пока среда выполнения не соберет мусор FileOutputStream, который выпал из области видимости. Это может означать, что другой процесс или поток, которому нужно открыть файл для записи, не сможет этого сделать, пока не будет собран экземпляр FileOutputStream.

4
ответ дан 27 November 2019 в 23:06
поделиться

Я сам предпочитаю использовать метод #1, он логически проще для чтения, а также логически более похож на то, что мы пытаемся сделать. (если происходит что-то плохое, выйдите из функции СЕЙЧАС, не передавайте go, не собирайте $200)

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

0
ответ дан 27 November 2019 в 23:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: