Поддерживаемость булевой логики - есть ли необходимость в вложении операторов?


11

Что из этого лучше для ремонтопригодности?

if (byteArrayVariable != null)
    if (byteArrayVariable .Length != 0)
         //Do something with byteArrayVariable 

ИЛИ ЖЕ

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

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

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

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

Спасибо.


1
Первая форма имеет риск болтаться
JBRWilkinson

просто интересно, на каком языке ты работаешь?
STW

@STW - мы используем C # и у нас есть некоторый устаревший код на Delphi.
Vaccano

2
хорошо знать. Я не знаком с Delphi , но в C # вы можете быть уверены в &&и ||операторов , выполняющих оценку короткого замыкания. Переход с одного языка на другой обычно имеет некоторые недостатки, и хорошо быть твердым в том, чтобы делать обзоры кода, чтобы помочь уловить эти маленькие проблемы.
STW

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

Ответы:


30

Я думаю, что вторая форма в порядке, а также более четко представляет то, что вы пытаетесь сделать. Вы говорите, что...

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

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


12
Согласовано. С какой стати мне было бы все равно, если мой код будет работать так же при копировании на другой язык?
Тим Гудман

16

Я удивлен, что никто не упомянул это (так что, надеюсь, я не неправильно понял вопрос) - но оба отстой!

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

В этот момент вы бы сделали что-то вроде:

if ((byteArrayVariable == null) || (byteArrayVariable.Length != 0)) {
  return; // nothing to be done, leaving
}

// Conditions met, do something

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

Если в вашем коде много спагетти (многоуровневых методов, множества вложенных if с разбросанной между ними логикой и т. Д.), То вам следует сосредоточиться на большей проблеме приведения своего кода в форму, прежде чем возникнут небольшие стилистические проблемы. В зависимости от вашей среды и серьезности проблемы, есть несколько полезных инструментов (например, Visual Studio имеет функцию рефакторинга «Извлечь метод»).


Как упоминает Джим, еще есть место для дебатов о том, как структурировать ранний выход. Альтернативой тому, что выше, будет:

if (byteArrayVariable == null) 
  return; // nothing to be done, leaving

if (byteArrayVariable.Length != 0)
  return;

// Conditions met, do something

1
Я согласен с возвратом вместо переноса хорошей логики, но те же люди будут использовать тот же аргумент об использовании || что касается &&.
МВД

14

Не тратьте время на попытки поймать в ловушку все эти состояния. Если вы можете дисквалифицировать данные или условия, сделайте это как можно скорее.

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
// do something

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

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
if (NewCondition == false) Exit;
if (NewerCondition == true) Exit;
// do something

2
+1 Да, да, да. Такая простая логика не должна приводить к сложному коду. Ваша (спрашивающая) интуиция должна вам это рассказывать.
Работа

11

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

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

В этом случае они в значительной степени должны быть вместе. Они действуют как охранник, чтобы гарантировать, что вы не выполните операцию, которая приведет к исключению. Используя nested ifs, вы оставляете за собой право интерпретировать человека, следующего за вами, относительно того, представляют ли эти два элемента защиту из двух частей или какую-то другую логику ветвления. Объединив их в одно, ifя это заявляю. Намного сложнее подкрасться к операции там, где ее не должно быть.

Я всегда буду отдавать предпочтение четкому сообщению о своем намерении, а не чьему-то глупому утверждению, что оно «работает не на всех языках». Угадайте, что ... throwработает не во всех языках, значит ли это, что я не должен использовать его при кодировании на C # или Java и вместо этого должен полагаться на возвращаемые значения, чтобы сигнализировать, когда возникла проблема?

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


Более двух условий могут гарантировать их собственную функцию, которая возвращает ноль.
Работа

3

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

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


1

Если вы работаете в Delphi, первый способ, в общем, безопаснее. (Для данного примера не так уж много пользы от использования вложенных if.)

Delphi позволяет вам указать, будут ли булевы выражения вычисляться или «замыкаться» через переключатели компилятора. Выполнение пути # 1 (вложенные ifs) позволяет вам гарантировать, что второе предложение выполняется тогда и только тогда, когда (и строго после ) первое предложение оценивается как true.


1
За 23 года разработки Delphi я никогда не менял опцию «Complete boolean eval». Если бы я отправлял код в другое место, я бы поставил {$ BOOLEVAL OFF} или {$ B-} в устройство.
Джерри

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

@ Джерри: Согласен. Единственная причина, по которой вы, возможно, захотите получить полную оценку, - это побочные эффекты, а побочные эффекты в условных выражениях - плохая идея!
Лорен Печтел

Просто перечитайте мой комментарий 23 года должно быть 13! У меня нет Тардис
Джерри

1

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


2
Это потому, что VBA - ужасный язык
Фальмарри

@Falmarri, в нем есть что-то ужасное (и кое-что хорошее). Я бы многое исправил, если бы у меня были мои барабанщики.

2
Отсутствие логической оценки короткого замыкания является более чем унаследованной вещью. Понятия не имею, почему они не начали с этого. VB.NET-исправление OrElse и AndAlso несколько раздражает, но, пожалуй, единственная возможность справиться с этим, не нарушая обратной совместимости.
JohnFx 19.10.10

1

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


0

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

В некоторых языках второй вариант работает лучше, так что тогда это будет очевидный выбор.


0

Я с вами; Я делаю это вторым способом. Для меня это логически понятнее. Беспокойство о том, что некоторые языки будут выполнять вторую часть, даже если первый оценивается как ложное, кажется мне немного глупым, поскольку я никогда в своей жизни не писал программу, которая работала на «некоторых языках»; мой код всегда, кажется, существует на определенном языке, который может справиться с этой конструкцией или не может. (И если я не уверен, может ли конкретный язык справиться с этим, это то, что мне действительно нужно выучить, не так ли.)

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


0

Я предпочитаю второй вариант, потому что он более читабелен.

Если логика, которую вы реализуете, является более сложной (проверка того, что строка не пуста и не пуста, это всего лишь пример), вы можете сделать полезный рефакторинг: извлечь булеву функцию. Я с @mipadi на замечание «Code Complete» («это не имеет значения, если все языки делают это ...») Если читатель вашего кода не заинтересован в поиске извлеченной функции, то порядок в котором булевы операнды оцениваются, становится для них спорным вопросом.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.