Так или иначе, в настоящее время я убежден, что я должен всегда стараться сделать свой код максимально надежным, даже если это означает добавление избыточного кода / проверок, которые, как я знаю , не будут сейчас полезны, но они может быть х количество лет по линии.
Например, в настоящее время я работаю над мобильным приложением с этим фрагментом кода:
public static CalendarRow AssignAppointmentToRow(Appointment app, List<CalendarRow> rows)
{
//1. Is rows equal to null? - This will be the case if this is the first appointment.
if (rows == null) {
rows = new List<CalendarRow> ();
}
//2. Is rows empty? - This will be the case if this is the first appointment / some other unknown reason.
if(rows.Count == 0)
{
rows.Add (new CalendarRow (0));
rows [0].Appointments.Add (app);
}
//blah...
}
Что касается конкретно второго раздела, я знаю, что если первый раздел верен, то второй раздел также будет верным. Я не могу придумать причину, по которой первый раздел будет ложным, а второй - истинным, что делает второе if
утверждение избыточным.
Однако в будущем может возникнуть ситуация, когда это второе if
утверждение действительно необходимо по известной причине.
Некоторые люди могут сначала посмотреть на это и подумать, что я программирую, думая о будущем, что, безусловно, хорошо. Но я знаю несколько случаев, когда этот вид кода «скрывал» ошибки от меня. Это значит, что мне потребовалось еще больше времени, чтобы понять, почему функция xyz
работает, abc
когда она должна работать def
.
С другой стороны, были также многочисленные случаи, когда этот вид кода значительно облегчал улучшение кода с помощью новых поведений, потому что мне не нужно возвращаться и проверять наличие всех соответствующих проверок.
Существуют ли общие правила для такого кода? (Мне также было бы интересно узнать, будет ли это хорошей или плохой практикой?)
NB: Это можно считать похожим на этот вопрос, однако, в отличие от этого вопроса, я хотел бы получить ответ, предполагая, что нет крайних сроков.
TLDR: Должен ли я зайти так далеко, чтобы добавить избыточный код, чтобы сделать его потенциально более надежным в будущем?
if(rows.Count == 0)
этого никогда не произойдет, тогда вы можете вызвать исключение, когда это произойдет, и проверить, почему ваше предположение оказалось неверным.rows
никогда не быть нулевым? Нет веской причины, по крайней мере в .NET, для коллекции быть нулевой. Пусто , конечно, но не ноль . Я бы выбросил исключение, если оноrows
равно нулю, потому что это означает, что вызывающая сторона имеет ошибку в логике.Ответы:
В качестве упражнения сначала проверим вашу логику. Хотя, как мы увидим, у вас больше проблем, чем у любой логической проблемы.
Назовите первое условие A и второе условие B.
Вы сначала говорите:
То есть: A подразумевает B или, в более общих чертах
(NOT A) OR B
А потом:
То есть:
NOT((NOT A) AND B)
. Применить закон Деморгана, чтобы получить,(NOT B) OR A
что Б подразумевает А.Следовательно, если оба ваших утверждения верны, то A подразумевает B, а B подразумевает A, что означает, что они должны быть равны.
Поэтому да, проверки являются излишними. Похоже, у вас есть четыре пути кода через программу, но на самом деле у вас есть только два.
Итак, теперь вопрос: как написать код? Реальный вопрос: что такое заявленный контракт метода ? Вопрос о том, являются ли условия излишними, - красная сельдь. Реальный вопрос заключается в том, «разработал ли я разумный контракт, и четко ли мой метод реализует этот контракт?»
Давайте посмотрим на объявление:
Это общедоступно, поэтому оно должно быть устойчивым к неверным данным от произвольных абонентов.
Он возвращает значение, поэтому оно должно быть полезно для его возвращаемого значения, а не для его побочного эффекта.
И все же название метода - глагол, предполагающий, что это полезно для его побочного эффекта.
Контракт параметра списка:
Этот контракт безумен . Представьте себе написание документации для этого! Представьте себе написание тестовых случаев!
Мой совет: начни сначала. Этот API имеет интерфейс конфетного автомата, написанный на всем протяжении этого. (Выражение взято из старой истории о кондитерских машинах в Microsoft, где и цены, и выбор являются двузначными числами, и очень легко набрать «85», что является ценой товара 75, и вы получите неправильный предмет. Забавный факт: да, я действительно сделал это по ошибке, когда пытался получить жвачку из торгового автомата в Microsoft!)
Вот как разработать разумный контракт:
Сделайте ваш метод полезным для его побочного эффекта или его возвращаемого значения, а не для обоих.
Не принимайте изменяемые типы в качестве входных данных, например списки; если вам нужна последовательность информации, возьмите IEnumerable. Только читать последовательность; не пишите в переданную коллекцию, если не очень ясно, что это контракт метода. Принимая IEnumerable, вы отправляете вызывающему сообщение, что не собираетесь изменять его коллекцию.
Никогда не принимайте нули; нулевая последовательность - мерзость. Требовать от вызывающей стороны пропускать пустую последовательность, если это имеет смысл, никогда не обнулять.
Сбой сразу, если вызывающий абонент нарушает ваш контракт, чтобы научить их тому, что вы имеете в виду бизнес, и чтобы они улавливали свои ошибки при тестировании, а не на производстве.
Дизайн контракта сначала должен быть максимально разумным, а затем четко выполнять контракт. Это путь к будущему дизайну.
Теперь я говорил только о вашем конкретном случае, и вы задали общий вопрос. Итак, вот несколько дополнительных общих советов:
Если есть факт, который вы, как разработчик, можете сделать вывод, а компилятор не может, то используйте утверждение для документирования этого факта. Если другой разработчик, например будущий вы или один из ваших коллег, нарушит это предположение, тогда утверждение скажет вам.
Получить тестовый инструмент покрытия. Убедитесь, что ваши тесты охватывают каждую строку кода. Если есть открытый код, то либо у вас пропущен тест, либо у вас мертвый код. Мертвый код на удивление опасен, потому что обычно он не предназначен для мертвых! Невероятно ужасный недостаток безопасности Apple "goto fail" пары лет назад сразу приходит на ум.
Получить инструмент статического анализа. Черт возьми, получи несколько; Каждый инструмент имеет свою особую специализацию, и ни один из них не является надмножеством других. Обратите внимание на то, когда он говорит вам, что существует недоступный или избыточный код. Опять же, это скорее всего ошибки.
Если это звучит так, как я говорю: во-первых, хорошо спроектируйте код, а во-вторых, протестируйте его, чтобы убедиться, что он правильный сегодня, ну, вот что я говорю. Выполнение этих вещей значительно облегчит будущее; самая сложная часть о будущем связана со всем глючным машинным кодом, написанным людьми в прошлом. Сделайте это правильно сегодня, и в будущем расходы будут ниже.
источник
CompareExchange
без такой возможности!), И даже в несовпадающих сценариях что-то типа «добавить запись, если она не существует, и вернуть либо переданный в запись или тот, который существовал "может быть более удобным, чем любой подход, который не использует ни побочный эффект, ни возвращаемое значение.То, что вы делаете в коде, который вы демонстрируете выше, это не столько надежное будущее, сколько защитное кодирование .
Оба
if
утверждения проверяют разные вещи. Оба являются подходящими тестами в зависимости от ваших потребностей.Раздел 1 проверяет и исправляет
null
объект. Примечание: при создании списка не создаются дочерние элементы (например,CalendarRow
).Раздел 2 проверяет и исправляет ошибки пользователя и / или реализации. То, что у вас есть
List<CalendarRow>
, не означает, что у вас есть какие-либо элементы в списке. Пользователи и разработчики будут делать то, что вы не можете себе представить, только потому, что им позволено делать это, имеет ли это смысл для вас или нет.источник
Я думаю, этот вопрос в основном не по вкусу. Да, это хорошая идея, чтобы написать надежный код, но код в вашем примере является небольшим нарушением принципа KISS (как будет много такого «будущего» кода).
Я лично, вероятно, не стал бы делать код пуленепробиваемый в будущем. Я не знаю будущего, и поэтому любой такой «будущий пуленепробиваемый» код обречен на неудачу, когда будущее все равно наступит.
Вместо этого я предпочел бы другой подход: сделать предположения, которые вы делаете явным, используя
assert()
макрос или аналогичные средства. Таким образом, когда за дверью приходит будущее, оно точно скажет вам, где ваши предположения больше не держатся.источник
Еще один принцип, о котором вы могли бы подумать, это идея быстрого провала . Идея состоит в том, что когда что-то идет не так в вашей программе, вы хотите сразу же полностью остановить программу, по крайней мере, пока вы ее разрабатываете, прежде чем выпускать. В соответствии с этим принципом, вы хотите написать много проверок, чтобы убедиться, что ваши предположения верны, но серьезно подумайте о том, чтобы ваша программа остановилась, когда допущения будут нарушены.
Проще говоря, если в вашей программе есть даже небольшая ошибка, вы хотите, чтобы она полностью вылетала во время просмотра!
Это может показаться нелогичным, но позволяет вам обнаруживать ошибки как можно быстрее во время рутинной разработки. Если вы пишете кусок кода и думаете, что он закончен, но он падает, когда вы тестируете его, нет сомнений, что вы еще не закончили. Кроме того, большинство языков программирования предоставляют вам отличные инструменты отладки, которые проще всего использовать, когда ваша программа полностью падает, вместо того, чтобы пытаться делать все возможное после ошибки. Самый большой и наиболее распространенный пример - это то, что если вы потерпели крах вашей программы, вызвав необработанное исключение, сообщение об исключении сообщит вам невероятное количество информации об ошибке, в том числе о том, какая строка кода завершилась неудачно, и путь через ваш код, который прошла программа. его путь к этой строке кода (трассировки стека).
Для большего количества мыслей прочитайте это короткое эссе: не прибивайте свою программу в вертикальном положении
Это имеет отношение к вам, потому что, возможно, иногда проверяемые вами проверки выполняются, потому что вы хотите, чтобы ваша программа продолжала работать даже после того, как что-то пошло не так. Например, рассмотрим краткую реализацию последовательности Фибоначчи:
Это работает, но что, если кто-то передает отрицательное число в вашу функцию? Тогда это не сработает! Поэтому вы захотите добавить проверку, чтобы убедиться, что функция вызывается с неотрицательным вводом.
Может быть заманчиво написать такую функцию:
Однако, если вы сделаете это, то позже случайно вызовете свою функцию Фибоначчи с отрицательным входом, вы никогда этого не поймете! Хуже того, ваша программа, вероятно, продолжит работать, но начнет давать бессмысленные результаты, не давая вам никаких подсказок о том, где что-то пошло не так. Это самые трудные для исправления ошибки.
Вместо этого лучше написать чек следующим образом:
Теперь, если вы когда-нибудь случайно вызовете функцию Фибоначчи с отрицательным входом, ваша программа немедленно остановится и сообщит вам, что что-то не так. Более того, предоставляя вам трассировку стека, программа сообщит вам, какая часть вашей программы пыталась выполнить функцию Фибоначчи неправильно, предоставляя вам отличную отправную точку для отладки того, что не так.
источник
Вы должны добавить избыточный код? Нет.
Но то, что вы описываете, не является избыточным кодом .
То, что вы описываете, - это программирование для защиты от вызова кода, нарушающего предварительные условия вашей функции. Делаете ли вы это или просто предоставляете пользователям право самостоятельно читать документацию и избегать этих нарушений, это совершенно субъективно.
Лично я большой сторонник этой методологии, но, как и во всем, вы должны быть осторожны. Взять, к примеру, C ++
std::vector::operator[]
. Отложив на время реализацию VS в режиме отладки, эта функция не выполняет проверку границ. Если вы запрашиваете элемент, который не существует, результат не определен. Пользователь должен предоставить действительный векторный индекс. Это вполне преднамеренно: вы можете «включить» проверку границ, добавив ее на сайт вызова, но еслиoperator[]
реализация будет выполнять ее, вы не сможете «отказаться». Как функция довольно низкого уровня, это имеет смысл.Но если бы вы писали
AddEmployee(string name)
функцию для какого-то высокоуровневого интерфейса, я бы полностью ожидал, что эта функция по крайней мере вызовет исключение, если вы предоставите пустое значениеname
, а также это предварительное условие будет задокументировано непосредственно перед объявлением функции. Возможно, вы не предоставляете необработанный пользовательский ввод для этой функции сегодня, но сделать ее «безопасной» таким образом означает, что любые нарушения предварительных условий, которые могут появиться в будущем, могут быть легко диагностированы, вместо того, чтобы потенциально вызвать цепочку домино с труднодоступными данными. обнаруживать ошибки. Это не избыточность: это усердие.Если бы мне пришлось придумать общее правило (хотя, как правило, я стараюсь их избегать), я бы сказал, что функция, которая удовлетворяет любому из следующего:
... может извлечь выгоду из защитного программирования. В других случаях вы все еще можете записывать
assert
ионы, которые срабатывают во время тестирования, но отключаются в сборках релиза, чтобы еще больше повысить вашу способность находить ошибки.Эта тема более подробно рассматривается в Википедии ( https://en.wikipedia.org/wiki/Defensive_programming ).
источник
Здесь приведены две из десяти программных заповедей:
Ты не должен предполагать, что ввод правильный
Ты не должен делать код для будущего использования
Здесь проверка на ноль - это не «создание кода для будущего использования». Создание кода для будущего использования - это такие вещи, как добавление интерфейсов, потому что вы думаете, что они могут быть полезны "когда-нибудь". Другими словами, заповедь не добавлять слои абстракции, если они не нужны прямо сейчас.
Проверка на ноль не имеет ничего общего с будущим использованием. Это связано с заповедью № 1: не думайте, что ввод будет правильным. Никогда не предполагайте, что ваша функция получит некоторое подмножество ввода. Функция должна отвечать логически, независимо от того, насколько ложными и испорченными являются входные данные.
источник
Thou shall not make code for future use
столкнулся с вопросами ремонтопригодности рано , несмотря на интуитивной логики заповеди. Я обнаружил, что в реальном кодировании эта заповедь эффективна только в коде, где вы управляете списком функций и сроками, и убедитесь, что вам не нужен будущий код для их достижения ... то есть никогда.Определение «избыточного кода» и «YAGNI» часто зависит от того, как далеко вы смотрите в будущее.
Если у вас возникла проблема, вы склонны писать будущий код таким образом, чтобы избежать этой проблемы. Другой программист, который не сталкивался с этой конкретной проблемой, может счесть ваш код избыточным.
Мое предложение состоит в том, чтобы отслеживать, сколько времени вы тратите на «вещи, которые еще не ошиблись», если его загрузка и ваши коллеги запускают функции быстрее, чем вы, а затем уменьшите ее.
Однако, если вы похожи на меня, я ожидаю, что вы просто напечатали все это «по умолчанию», и это на самом деле больше не занимало вас.
источник
Рекомендуется задокументировать любые предположения о параметрах. И это хорошая идея, чтобы убедиться, что ваш клиентский код не нарушает эти предположения. Я бы сделал это:
[Предполагая, что это C #, Assert, возможно, не лучший инструмент для работы, так как он не скомпилирован в выпущенном коде. Но это спор на другой день.]
Почему это лучше, чем то, что вы написали? Ваш код имеет смысл, если в будущем, когда ваш клиент изменился, когда клиент перейдет в пустой список, правильнее всего будет добавить первую строку и добавить приложение к его встречам. Но откуда вы знаете, что это будет так? Лучше сделать меньше предположений о будущем.
источник
Оцените стоимость добавления этого кода сейчас . Это будет относительно дешево, потому что все это свежо в вашем уме, так что вы сможете сделать это быстро. Добавление модульных тестов необходимо - нет ничего хуже, чем использовать какой-либо метод год спустя, он не работает, и вы понимаете, что он был сломан с самого начала и никогда не работал.
Оцените стоимость добавления этого кода, когда это необходимо. Это будет дороже, потому что вы должны вернуться к коду, запомнить все это, и это намного сложнее.
Оцените вероятность того, что дополнительный код действительно понадобится. Тогда делай математику.
С другой стороны, код, полный предположений "X никогда не случится", ужасен для отладки. Если что-то работает не так, как задумано, это означает либо глупую ошибку, либо неверное предположение. Ваше «X никогда не случится» является предположением, и при наличии ошибки это подозрительно. Что заставляет следующего разработчика тратить на это время. Обычно лучше не полагаться на такие предположения.
источник
Основной вопрос здесь: «что произойдет, если вы делаете / не делаете»?
Как указывали другие, этот вид защитного программирования хорош, но иногда он опасен.
Например, если вы укажете значение по умолчанию, вы продолжите работу программы. Но теперь программа может делать не то, что вы хотите. Например, если он записывает этот пустой массив в файл, вы, возможно, теперь превратили свою ошибку из «сбоев, потому что я предоставил ноль случайно» в «очистку строк календаря, потому что я поставил ноль случайно». (например, если вы начинаете удалять вещи, которых нет в списке в той части, которая гласит «// бла»)
Ключ для меня никогда не портит данные . Позвольте мне повторить это; НИКОГДА. Коррумпированы. ДАННЫЕ. Если ваша программа исключений, вы получите отчет об ошибке, которую можно исправить; если он записывает неверные данные в файл, который будет позже, вы должны сеять землю солью.
Все ваши «ненужные» решения должны приниматься с учетом этой предпосылки.
источник
Здесь вы имеете дело с интерфейсом. Добавив поведение «когда ввод есть
null
, инициализируйте ввод», вы фактически расширили интерфейс метода - теперь вместо того, чтобы всегда работать с действительным списком, вы заставили его «исправить» ввод. Является ли это официальной или неофициальной частью интерфейса, вы можете поспорить, что кто-то (скорее всего, включая вас) будет использовать это поведение.Интерфейсы должны быть простыми, и они должны быть относительно стабильными, особенно в
public static
методе. Вы получаете немного свободы в частных методах, особенно в методах частных экземпляров. Неявно расширяя интерфейс, вы на практике сделали свой код более сложным. Теперь представьте, что вы на самом деле не хотите использовать этот путь кода, поэтому избегайте его. Теперь у вас есть немного непроверенного кода, который притворяется, будто это часть поведения метода. И я могу вам сказать прямо сейчас, что, возможно, в нем есть ошибка: когда вы передаете список, этот список видоизменяется методом. Однако, если вы этого не сделаете, вы создаете локальныйсписок, и выбросить его позже. Это противоречивое поведение, которое заставит вас плакать через полгода, когда вы пытаетесь отследить скрытую ошибку.Вообще, защитное программирование - довольно полезная вещь. Но пути кода для защитных проверок должны быть проверены, как и любой другой код. В таком случае они усложняют ваш код без причины, и вместо этого я бы выбрал такую альтернативу:
Вы не хотите, чтобы ввод был
rows
нулевым, и вы хотите, чтобы ошибка стала очевидной для всех ваших абонентов как можно скорее .Есть много ценностей, которые вам нужно переделывать при разработке программного обеспечения. Даже надежность сама по себе является очень сложным качеством - например, я бы не посчитал вашу защиту более надежной, чем создание исключения. Исключения довольно удобны для того, чтобы дать вам безопасное место для повторной попытки из безопасного места - проблемы с повреждением данных, как правило, гораздо сложнее отследить, чем распознать проблему на ранней стадии и безопасно ее обработать. В конце концов, они, как правило, создают у вас иллюзию надежности, и затем через месяц вы замечаете, что десятая часть ваших назначений пропала, потому что вы никогда не замечали, что обновился другой список. Уч.
Удостоверьтесь, чтобы различить два. Защитное программирование - это полезный метод для выявления ошибок в месте, где они наиболее актуальны, что значительно помогает при отладке и обеспечивает хорошую обработку исключений, предотвращая «скрытую коррупцию». Ошибка рано, ошибка быстро. С другой стороны, то, что вы делаете, больше похоже на «сокрытие ошибок» - вы манипулируете вводными данными и делаете предположения о том, что имел в виду вызывающий объект. Это очень важно для кода, ориентированного на пользователя (например, проверка орфографии), но вы должны быть осторожны, когда видите код, обращенный к разработчику.
Основная проблема заключается в том, что какую бы абстракцию вы ни делали, она будет просачиваться («Я хотел напечатать снова, а не предвкушать! Глупая проверка орфографии!»), А код для обработки всех особых случаев и исправлений по-прежнему остается кодом нужно поддерживать и понимать, а код нужно тестировать. Сравните усилия, направленные на то, чтобы убедиться, что ненулевой список передан, с исправлением ошибки, которая возникла у вас год спустя, в процессе производства - это не хороший компромисс. В идеальном мире вы бы хотели, чтобы каждый метод работал исключительно со своим собственным вводом, возвращая результат и не изменяя глобального состояния. Конечно, в реальном мире вы найдете множество случаев, когда это несамое простое и ясное решение (например, при сохранении файла), но я считаю, что поддержание чистоты методов, когда у них нет причин читать или манипулировать глобальным состоянием, значительно упрощает анализ кода. Это также дает вам больше естественных точек для разделения ваших методов :)
Это не значит, что все неожиданное должно привести к сбою приложения, а наоборот. Если вы правильно используете исключения, они, естественно, образуют безопасные точки обработки ошибок, где вы можете восстановить стабильное состояние приложения и позволить пользователю продолжить то, что они делают (в идеале, избегая потери данных для пользователя). В этих точках обработки вы увидите возможности для устранения проблем («Номер заказа 2212 не найден. Вы имели в виду 2212b?») Или предоставление пользователю контроля («Ошибка подключения к базе данных. Попробуйте еще раз?»). Даже если такой опции нет, по крайней мере, это даст вам шанс, что ничего не испортилось - я начал ценить код, который использует
using
иtry
...finally
намного больше, чемtry
...catch
Это дает вам много возможностей для поддержания инвариантов даже в исключительных условиях.Пользователи не должны терять свои данные и работать. Это все равно должно быть сбалансировано с затратами на разработку и т. Д., Но это довольно хорошее общее руководство (если пользователи решают, покупать ваше программное обеспечение или нет - внутреннее программное обеспечение обычно не имеет такой роскоши). Даже сбой всего приложения становится намного меньшей проблемой, если пользователь может просто перезапустить и вернуться к тому, что он делал. Это настоящая надежность - Word сохраняет вашу работу все время, не повреждая документ на диске, и дает вам возможностьвосстановить эти изменения после перезапуска Word после сбоя. Это лучше, чем отсутствие ошибки в первую очередь? Вероятно, нет - хотя не забывайте, что работа, потраченная на выявление редкой ошибки, может быть лучше проведена повсюду. Но это намного лучше, чем альтернативы - например, поврежденный документ на диске, вся работа с момента последнего сохранения потеряна, документ автоматически заменяется изменениями до сбоя, которые оказались Ctrl + A и Delete.
источник
Я собираюсь ответить на это, основываясь на вашем предположении, что надежный код принесет вам «долгие годы». Если вашей целью являются долгосрочные выгоды, я бы поставил дизайн и ремонтопригодность выше надежности.
Компромисс между дизайном и надежностью - это время и фокус. Большинство разработчиков предпочли бы иметь набор хорошо спроектированного кода, даже если это означает прохождение через некоторые проблемные места и выполнение дополнительных условий или обработку ошибок. После нескольких лет использования, места, которые вам действительно нужны, вероятно, были определены пользователями.
Предполагая, что дизайн примерно одинакового качества, меньше кода легче поддерживать. Это не значит, что нам будет лучше, если вы позволите известным проблемам уйти на несколько лет, но добавление вещей, которые вам не нужны, усложняет задачу. Мы все посмотрели на устаревший код и нашли ненужные части. У вас должен быть высокий уровень изменяющего доверие кода, который работал годами.
Поэтому, если вы чувствуете, что ваше приложение спроектировано так, как может, простое в обслуживании и не содержит ошибок, найдите что-то лучше, чем добавить код, который вам не нужен. Это самое меньшее, что вы можете сделать из уважения ко всем другим разработчикам, которые много часов работают над бессмысленными функциями.
источник
Нет, ты не должен. И вы на самом деле отвечаете на свой вопрос, когда утверждаете, что этот способ кодирования может скрывать ошибки . Это не сделает код более устойчивым - скорее это сделает его более подверженным ошибкам и усложнит отладку.
Вы заявляете свои текущие ожидания в отношении
rows
аргумента: либо он нулевой, либо в противном случае в нем есть хотя бы один элемент. Таким образом, вопрос заключается в следующем: является ли хорошей идеей написать код для дополнительной обработки неожиданного третьего случая, в которомrows
нет элементов?Ответ - нет. Вы должны всегда выдавать исключение в случае неожиданного ввода. Учтите это: если некоторые другие части кода нарушают ожидания (т. Е. Контракт) вашего метода, это означает, что есть ошибка . Если есть ошибка, вы хотите знать ее как можно раньше, чтобы вы могли ее исправить, и исключение поможет вам сделать это.
В настоящее время код пытается угадать, как исправить ошибку, которая может существовать или не существовать в коде. Но даже если есть ошибка, вы не можете знать, как полностью исправить ее. Ошибки по определению имеют неизвестные последствия. Возможно, какой-то код инициализации не был запущен так, как ожидалось, и может иметь много других последствий, кроме просто отсутствующей строки.
Итак, ваш код должен выглядеть так:
Примечание. В некоторых конкретных случаях имеет смысл «угадать», как обрабатывать неверный ввод, а не просто генерировать исключение. Например, если вы обрабатываете внешний ввод, вы не можете контролировать его. Веб-браузеры являются печально известным примером, потому что они пытаются изящно обрабатывать любой вид искаженного и неправильного ввода. Но это имеет смысл только с внешним вводом, а не с вызовами из других частей программы.
Изменить: Некоторые другие ответы утверждают, что вы делаете защитное программирование . Я не согласен. Защитное программирование означает, что вы не доверяете автоматически вводимым данным. Таким образом, проверка параметров (как указано выше) является защитной техникой программирования, но это не означает, что вы должны изменить неожиданный или неверный ввод путем угадывания. Надежный защитный подход заключается в проверке ввода и последующем генерировании исключения в случае неожиданного или неверного ввода.
источник
Вы не должны добавлять избыточный код в любое время.
Вы не должны добавлять код, который нужен только в будущем.
Вы должны убедиться, что ваш код ведет себя хорошо, независимо от того, что происходит.
Определение «хорошо себя вести» зависит от ваших потребностей. Одна техника, которую я люблю использовать, это исключения из «паранойи». Если я на 100% уверен, что определенный случай никогда не произойдет, я все равно запрограммирую исключение, но я делаю это так, чтобы: а) четко сказать всем, что я никогда не ожидаю этого, и б) четко отображается и регистрируется, и таким образом, не приводит к ползучей коррупции в дальнейшем.
Пример псевдокода:
Это ясно говорит о том, что я на 100% уверен, что я всегда могу открыть, написать или закрыть файл, т. Е. Я не хожу до степени создания сложной обработки ошибок. Но если (нет: когда) я не могу открыть файл, моя программа все равно будет с ошибкой контролируемым образом.
Пользовательский интерфейс, конечно, не будет отображать такие сообщения для пользователя, они будут регистрироваться внутри вместе с трассировкой стека. Опять же, это внутренние исключения «паранойи», которые просто гарантируют, что код «останавливается», когда происходит что-то неожиданное. Этот пример немного надуманный, на практике я бы, конечно, реализовал реальную обработку ошибок при открытии файлов, поскольку это происходит регулярно (неправильное имя файла, USB-накопитель монтируется только для чтения, что угодно).
Как отмечалось в других ответах, очень важный связанный поисковый термин будет «быстро проваливаться» и очень полезен для создания надежного программного обеспечения.
источник
Здесь много слишком сложных ответов. Вы, вероятно, задавали этот вопрос, потому что вы не чувствовали себя правильно в отношении этого кода, но не знали, почему или как это исправить. Поэтому я отвечаю, что проблема, скорее всего, в структуре кода (как всегда).
Во-первых, заголовок метода:
Назначить встречу для какой строки? Это должно быть ясно сразу из списка параметров. Без каких - либо дополнительных знаний, я бы ожидать , что метод PARAMS выглядеть следующим образом :
(Appointment app, CalendarRow row)
.Далее «входные проверки»:
Это фигня.
rows
в метод, вероятно, является неправильной (см. комментарий выше), то он не должен отвечать за метод, вызываемыйAssignAppointmentToRow
для манипулирования строками любым другим способом, кроме назначения назначения где-либо.Но сама концепция назначения встреч где-то странна (если только это не часть кода GUI). Ваш код, кажется, содержит (или, по крайней мере, пытается) явную структуру данных, представляющую календарь (то есть
List<CalendarRows>
<- который должен быть определен какCalendar
где-то, если вы хотите пойти по этому пути, то вы бы переходилиCalendar calendar
к своему методу). Если вы пойдете по этому пути, я бы ожидал, что онcalendar
будет предварительно заполнен слотами, в которые вы помещаете (назначаете) встречи после этого (например,calendar[month][day] = appointment
будет соответствующий код). Но тогда вы также можете вообще отделить структуру календаря от основной логики и просто иметь,List<Appointment>
гдеAppointment
объекты содержат атрибутdate
, И затем, если вам нужно визуализировать календарь где-нибудь в графическом интерфейсе, вы можете создать эту структуру «явного календаря» непосредственно перед рендерингом.Я не знаю деталей вашего приложения, поэтому некоторые из них, возможно, не применимы к вам, но обе эти проверки (в основном вторая) говорят мне, что что-то не так с разделением проблем в вашем коде.
источник
Для простоты, давайте предположим, что вам или в конечном итоге понадобится этот кусок кода за N дней (не позже или раньше) или он вообще не понадобится.
псевдокод:
Факторы для
C_maint
:C_maint
ожидается отрицательныйC_maint
. В этом случае требуется более сложная формула с большим количеством переменных, напримерN
.Такую большую вещь, которая просто отягощает код и может понадобиться только через 2 года с низкой вероятностью, следует пропустить, но небольшая вещь, которая также выдвигает полезные утверждения и на 50%, что она понадобится через 3 месяца, должна быть реализована ,
источник