Необходимо ли всегда писать код для еще случаев, которых “никогда не может происходить”?

Возьмите некоторый код как

if (person.IsMale()) {
    doGuyStuff();
} else {
    doGirlStuff();
}

Если это записано, чтобы явно проверить если person.isFemale(), и затем еще добавьте новое, которое выдает исключение? Возможно, Вы проверяете значения в перечисление или что-то как этот. Вы думаете, что никто не добавит новые элементы к перечислению, но кто знает? "Никогда не может происходить", походит на известные последние слова.

19
задан Doorknob 15 December 2014 в 19:14
поделиться

14 ответов

Думаю, вы сами ответили на свой вопрос. Если вы знаете , вы никогда не увидите дополнительных значений:

bool isSet = ...
if (isSet)
{
    return foo;
}
return bar;

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

9
ответ дан 30 November 2019 в 03:33
поделиться

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

8
ответ дан 30 November 2019 в 03:33
поделиться

Думаю, это зависит от типа вашего объекта. Если он строго логический (или вообще двоичный), то, как вы уже догадались, второй тест избыточен. В любом другом случае будет проведен второй тест.

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

1
ответ дан 30 November 2019 в 03:33
поделиться

@Michael Petrotta - ваш фрагмент кода неверен, так как действие DoY () выполняется без учета истинного значения условия.

(извините, пока не могу добавлять комментарии ...)

1
ответ дан 30 November 2019 в 03:33
поделиться

Помните, что перечисления в 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
ответ дан 30 November 2019 в 03:33
поделиться

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

Когда клиент звонит и говорит: «Привет, я получаю сообщение об ошибке типа« не разрешен », а в нем говорится:« Тип утка не разрешен », мы быстро нашли причину проблемы и смогли ее решить.

{ {1}}
3
ответ дан 30 November 2019 в 03:33
поделиться

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

0
ответ дан 30 November 2019 в 03:33
поделиться

Лично я бы написал строку else как:

} else /*if (person.IsFemale())*/ {

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

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

0
ответ дан 30 November 2019 в 03:33
поделиться

Если вы хотите указать блок кода, который «не может произойти», используйте

assert (false);
2
ответ дан 30 November 2019 в 03:33
поделиться

Вопрос зависит от вашего контекста - сможет ли кто-нибудь другой когда-либо расширить ваш объект Person? Когда андроиды станут законными Людьми, вы можете обнаружить, что и isMale(), и isFemale() могут быть ложными.

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

0
ответ дан 30 November 2019 в 03:33
поделиться

Если вы внимательно прочитаете некоторые книги по формальным методам, то они предлагают сделать следующее.

  1. Определите постусловие

    isMale && doGuyStuff || isFemale && doGirlStuff.
    
  2. Выведем несколько утверждений-кандидатов, которые приведут к этому постусловию

    if isMale: doGuyStuff
    

    Это доказуемо приводит к некоторому постусловию

    if isFemale: doGirlStuff
    

    Это доказуемо приводит к некоторым из постусловий

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

  3. В итоге получается следующее:

    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, это будет означать, что вы неправильно посчитали и неправильно вывели утверждения из условий. Или вы неправильно определили постусловие в первую очередь.

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

8
ответ дан 30 November 2019 в 03:33
поделиться

Поскольку ответов так много, я просто покажу вам, как выглядит переусердствование: (в 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.
}

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

0
ответ дан 30 November 2019 в 03:33
поделиться

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

if(male)
{
  ...
}
else
{
  ...
}

// other stuff that nobody cares about...
assert(male||female);
0
ответ дан 30 November 2019 в 03:33
поделиться

Если это НЕ МОЖЕТ произойти в производственной среде (но может произойти во время разработки), я использую assert :

Если это НЕ ДОЛЖНО произойти в производственной среде, но возможно, я либо верну ошибку , либо выдаю исключение .

Кстати, вот небольшая хитрость, связанная с C ++, которую я где-то уловил; поскольку многие макросы ASSERT будут отображать свое выражение в окне сообщения, если утверждение неверно, вы можете использовать следующую форму для ветвей, которые никогда не должны выполняться:

if (person.IsMale())
{
    AdmitEntranceToManCave();
}
else
{
    ASSERT(!"A female has gotten past our defenses. EVERYBODY PANIC!");
}

Строковый литерал оценивается как (не-NULL) адрес, который является логически ИСТИНА. Таким образом, использование логического оператора НЕ делает его ЛОЖНЫМ.

4
ответ дан 30 November 2019 в 03:33
поделиться
Другие вопросы по тегам:

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