Возьмите некоторый код как
if (person.IsMale()) {
doGuyStuff();
} else {
doGirlStuff();
}
Если это записано, чтобы явно проверить если person.isFemale()
, и затем еще добавьте новое, которое выдает исключение? Возможно, Вы проверяете значения в перечисление или что-то как этот. Вы думаете, что никто не добавит новые элементы к перечислению, но кто знает? "Никогда не может происходить", походит на известные последние слова.
Думаю, вы сами ответили на свой вопрос. Если вы знаете , вы никогда не увидите дополнительных значений:
bool isSet = ...
if (isSet)
{
return foo;
}
return bar;
... тогда не беспокойтесь. Но если есть шанс, что возможных значений может быть больше, чем два, прикрывайте себя. Вы (или программист по обслуживанию через два года) будете за это благодарны.
Я считаю, что «никогда не может случиться» звучит хорошо, пока не укусит вас за задницу несколько месяцев спустя, после того как коллега добавил код, нарушив ваш исходный код. Так что для себя я бы удостоверился, что мои if твердые, даже если это кажется невозможным.
Думаю, это зависит от типа вашего объекта. Если он строго логический (или вообще двоичный), то, как вы уже догадались, второй тест избыточен. В любом другом случае будет проведен второй тест.
Однако даже при этом условии вы можете иметь «мысленный ярлык» для защиты от будущего расширения предметной области - просто предположите, что только одно значение является «истинным» значением, а все остальные значения по умолчанию равны «ложное» значение.
@Michael Petrotta - ваш фрагмент кода неверен, так как действие DoY () выполняется без учета истинного значения условия.
(извините, пока не могу добавлять комментарии ...)
Помните, что перечисления в C # могут вас укусить:
enum SwitchPosition
{
Off = 0,
On = 1
}
void SetLightStatus(SwitchPosition pos)
{
switch (pos)
{
case On:
TurnLightOn();
break;
case Off:
TurnLightOff();
break;
default:
// Fugedaboudit -- will never happen, right?
}
}
Неверно! Вызов SetLightPosition (2);
является допустимым и не будет выполняться во всех случаях в этом операторе переключения. Лучше всего выбросить HorrifyingSituationError
, как предлагает С. Лотт.
Я не могу писать код для своей компании, но было много случаев, когда наши программисты кодировали вещи для того случая, которого никогда не случится, и это пригодился при поиске и устранении проблем, о которых сообщил клиент. Такое часто случается с кодом, который мы получаем из наших зарубежных проектов.
Когда клиент звонит и говорит: «Привет, я получаю сообщение об ошибке типа« не разрешен », а в нем говорится:« Тип утка не разрешен », мы быстро нашли причину проблемы и смогли ее решить.
{ {1}}Если бы это никогда не могло произойти, то вам не нужно было бы писать для этого код.
Лично я бы написал строку else
как:
} else /*if (person.IsFemale())*/ {
Это обходится без для запуска функции (я бы не хотел тратить время на ее выполнение, если ее результаты не нужны), но оставляет важную документацию для будущих разработчиков. Теперь ясно, что эта ветвь условного выражения охватывает случай IsFemale
(в частности), а не случай ! IsMale
(в общем). По сути, вы делаете свои предположения «вслух», что снижает вероятность того, что будущие изменения неверно поймут, что вы делаете, и нарушат ваш код.
В наших встроенных системах может быть трудно анализировать и отлаживать сбои, поэтому мы часто включаем «невозможные» else
операторы и заставляем их выдавать сообщения об ошибках и генерировать исключение. Обычно это ограничивается сборками для разработки, и как только код становится стабильным, они удаляются.
Если вы хотите указать блок кода, который «не может произойти», используйте
assert (false);
Вопрос зависит от вашего контекста - сможет ли кто-нибудь другой когда-либо расширить ваш объект Person? Когда андроиды станут законными Людьми, вы можете обнаружить, что и isMale(), и isFemale() могут быть ложными.
Это особенно актуально, если код, который вы пишете, является модулем, который будет использоваться людьми вне вашей группы. Конечно, в этом случае вы можете пойти дальше, пропустить жестко закодированные тесты if и вызвать doGenderStuff() на объекте, созданном соответствующей фабрикой...
Если вы внимательно прочитаете некоторые книги по формальным методам, то они предлагают сделать следующее.
Определите постусловие
isMale && doGuyStuff || isFemale && doGirlStuff.
Выведем несколько утверждений-кандидатов, которые приведут к этому постусловию
if isMale: doGuyStuff
Это доказуемо приводит к некоторому постусловию
if isFemale: doGirlStuff
Это доказуемо приводит к некоторым из постусловий
Обратите внимание, что порядок не имеет значения. Действительно, все проще, если убрать любые предположения об упорядочивании.
В итоге получается следующее:
if isMale: doGuyStuff
elif isFemale: doGirlStuff
Обратите внимание, что здесь нет надлежащего использования оговорки else
. Вы никогда - в формальной деривации - не выведете условия else
. У вас всегда будут условия, которые являются положительными утверждениями: a && b || c && d
. Редко это будет a && b || !a && c
, но даже в этом случае вы обычно получаете явное условие !a
.
Формально, условие "невозможно иначе" должно быть ограничено выполнением чего-то вроде следующего.
if isMale: doGuyStuff
elif isFemale: doGirlStuff
else:
raise HorrifyingSituationError
Если у вас возникнет ошибка HorrifyingSituationError, это будет означать, что вы неправильно посчитали и неправильно вывели утверждения из условий. Или вы неправильно определили постусловие в первую очередь.
В любом случае, программа была разработана неправильно в глубоком и абсолютном смысле. Как правило, это не является сюрпризом. Обычно программа терпит впечатляющий крах при первой же попытке ее протестировать. Если только (как это часто бывает) вы не выбрали тестовые данные, которые отражают ошибки в вашем первоначальном определении постусловия. Даже в этом случае, как только вы столкнулись с этим исключением, вы можете легко отследить его и исправить навсегда.
Поскольку ответов так много, я просто покажу вам, как выглядит переусердствование: (в C++)
if(!DoOperation())
{
// Allocate for a message
char * pMsg = new char[256];
// Make sure the memory was allocated
if(!pMsg)
{
cout << PREDEFINED_OUT_OF_MEMORY_MESSAGE << endl;
exit(0);
}
if(!strcpy(pMsg,"Operation failed!"))
{
cout << PREDEFINED_STRCPY_FAILED_MESSAGE << endl;
exit(0);
}
// Now let the user know there was an error
ErrorDetailStructure * pError = new ErrorDetailStructure();
if(!pError)
{
cout << PREDEFINED_OUT_OF_MEMORY_MESSAGE << endl;
exit(0);
}
// Copy the message to the error structure
if(!strcpy(pError->pMessage,pMsg))
{
cout << PREDEFINED_STRCPY_FAILED_MESSAGE << endl;
exit(0);
}
// Alert the user - yes, ErrorDetailsStructure
// overloads operator<<
cout << pError;
delete pError; // the destructor frees pMessage member
// Now we need to log this error
some_file_pointer * pFile = OpenAFilePointer("/root/logfile");
if(!some_file_pointer)
{
cout << PREDEFINED_OPEN_FILE_ERROR_MESSAGE << endl;
exit(0);
}
some_file_pointer->WriteError("Something went wrong. Guess what it was.");
// Don't forget to free some_file_pointer
delete some_file_pointer;
exit(0); // Just quit. Give up.
}
Я получил массу удовольствия от этого. С этим так много проблем (а дизайн настолько плох), что я хорошо посмеялся, написав его.
Я бы сохранил код как можно более чистым и коротким, а затем добавил бы утверждения в места, которые не загрязняют чистоту :)
if(male)
{
...
}
else
{
...
}
// other stuff that nobody cares about...
assert(male||female);
Если это НЕ МОЖЕТ произойти в производственной среде (но может произойти во время разработки), я использую assert
:
Если это НЕ ДОЛЖНО произойти в производственной среде, но возможно, я либо верну ошибку , либо выдаю исключение .
Кстати, вот небольшая хитрость, связанная с C ++, которую я где-то уловил; поскольку многие макросы ASSERT будут отображать свое выражение в окне сообщения, если утверждение неверно, вы можете использовать следующую форму для ветвей, которые никогда не должны выполняться:
if (person.IsMale())
{
AdmitEntranceToManCave();
}
else
{
ASSERT(!"A female has gotten past our defenses. EVERYBODY PANIC!");
}
Строковый литерал оценивается как (не-NULL) адрес, который является логически ИСТИНА. Таким образом, использование логического оператора НЕ делает его ЛОЖНЫМ.