Как оборонительно действительно ли я должен программировать? [закрытый]

Используйте различных цветов .

Он генерирует палитру визуально различных цветов.

Отдельные цвета легко настраиваются:

  • Выберите количество цветов в палитре
  • Ограничьте оттенок определенным диапазоном
  • Ограничьте цветность ( насыщенность) до определенного диапазона
  • Ограничение освещенности до определенного диапазона
  • Настройка общего качества палитры

38
задан Noldorin 27 June 2009 в 17:23
поделиться

14 ответов

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

Изменить: этот ответ кажется несколько спорным, поэтому немного доработки: Защитное программирование означает «готовиться к неожиданностям» (или «быть параноиком»), и один из способов сделать это - выполнить множество проверок предварительных условий. Во многих случаях это хорошая практика, однако, как и во всех других практиках, затраты следует сопоставлять с выгодами.

Например, исключение «Не удалось получить соединение с фабрики» не дает никаких преимуществ, поскольку в нем ничего не говорится о , почему ] поставщик не может быть получен - и в самой следующей строке все равно будет выдано исключение, если поставщик равен нулю. Таким образом, стоимость проверки предварительных условий (время разработки и сложность кода) не оправдана.

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

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

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

Некоторые случаи, когда такое защитное программирование имеет смысл, - это если вы принимаете вводимые пользователем данные, и этот ввод может привести к ошибкам. Если, например, пользователь предоставляет URL-адрес в качестве входных данных, а приложение пытается извлечь что-то из этого URL-адреса, то очень важно, чтобы вы проверили, что URL-адрес выглядит правильно, и обработали любое исключение, которое может возникнуть в результате запроса. Это позволяет предоставить пользователю ценные отзывы.

31
ответ дан 27 November 2019 в 03:40
поделиться

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

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

Изменения Иэна кажутся мне разумными.

Если я использую систему и использую ее ненадлежащим образом, я хочу получить максимум информации о неправильном использовании. Например, если я забыл вставить некоторые значения в конфигурацию перед вызовом метода, мне нужно исключение InvalidOperationException с сообщением с подробным описанием моей ошибки, а не KeyNotFoundException / NullReferenceException.

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

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

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

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

Не забудьте проверить OutOfMemoryExceptions ... знаете, это может произойти.

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

Самым простым решением было бы использовать ORM. См. LLBLGen. Используя модель адаптера, вы можете переключаться между поставщиками данных, используя одни и те же бизнес-объекты. Он может генерировать код как для MySql, так и для Sql Server.

Это связано с тем, что если вы поймаете исключение, любые открытые блоки finally будут выполнены и, вероятно, вызовут больше исключений, которые скроют исходное исключение (или, что еще хуже, они удалят файлы в попытке вернуть какое-то состояние, возможно, неправильные файлы, возможно, даже важные файлы). Вы никогда не должны перехватывать исключение, указывающее на недопустимое состояние программы, с которым вы не знаете, как обработать - даже в блоке верхнего уровня main function try / catch . Обработка AppDomain.UnexpectedException и вызов Environment.FailFast - это другой (и более желанный) котелок с рыбой, потому что он останавливает finally блокирует выполнение, и если вы пытаемся остановить вашу программу и записать некоторую полезную информацию, не нанося дальнейшего ущерба,

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

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

Еще одна причина для быстрого отлова этих ошибок заключается в том, что они часто включают некоторые детали базы данных в свои сообщения, например «Нет такой таблицы. : ACCOUNTS_BLOCKED "или" Недействительный ключ пользователя: 234234 ". Если это распространяется на конечного пользователя, это плохо по нескольким причинам:

  1. Запутывает.
  2. Возможное нарушение безопасности.
  3. Смущает имидж вашей компании (представьте, что клиент читает сообщение об ошибке с грубой грамматикой).
1
ответ дан 27 November 2019 в 03:40
поделиться

Я бы закодировал это точно так же, как и вашу первую попытку.

Однако пользователь этой функции защитит объект подключения блоком USING.

Мне действительно не нравится перевод исключений, как и другие ваши версии, делает очень трудным выяснить, почему он сломался (База данных не работает? Нет разрешения на чтение файла конфигурации и т. д.?).

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

Мое практическое правило:

не ловите, если сообщение выброшенное исключение относится к вызывающий.

Таким образом, NullReferenceException не имеет соответствующего сообщения, я бы проверил, является ли оно нулевым, и выдал бы исключение с лучшим сообщением. ConfigurationErrorException имеет значение, поэтому я его не улавливаю.

Единственное исключение - если «контракт» GetConnection не извлекает строку подключения обязательно в файле конфигурации.

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

Другое решение - указать, что GetConnection не может генерировать (но может возвращать null), затем вы добавляете ExceptionHandler "в свой класс.

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

I don't think I'd write any of that null-reference checking logic - at least, not the way you've done it.

My programs that get configuration settings from the application configuration file check all of those settings at startup. I usually build a static class to contain the settings and reference that class's properties (and not the ConfigurationManager) elsewhere in the application. There are two reasons for this.

First, if the application isn't configured properly, it's not going to work. I'd rather know this at the moment the program reads the configuration file than at some point in the future when I try to create a database connection.

Second, checking the validity of configuration shouldn't really be the concern of the objects that rely on the configuration. There's no sense in making yourself insert checks everywhere throughout your code if you've already performed those checks up front. (There are exceptions to this, certainly - for instance, long-running applications where you need to be able to change the configuration while the program is running and have those changes reflected in the program's behavior; in programs like this you'll need to go to the ConfigurationManager whenever you need a setting.)

I wouldn't do the null-reference checking on the GetFactory and CreateConnection calls, either. How would you write a test case to exercise that code? You can't, because you don't know how to make those methods return null - you don't even know that it's possible to make those methods return null. So you've replaced one problem - your program can throw a NullReferenceException under conditions that you don't understand - with another, more significant one: under those same mysterious conditions, your program will run code that you haven't tested.

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

Хотел бы я заставить мою команду писать такой код. Большинство людей даже не понимают смысла защитного программирования. Лучшее, что они делают, - это обернуть весь метод оператором try catch и позволить всем исключениям обрабатывать общий блок исключений!

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

Помните, когда вы используете API фреймворка .net, чего вы от него ждете? Что кажется естественным? Сделайте то же самое со своим кодом.

Я знаю, что это требует времени. Но тогда качество имеет свою цену.

PS: Вам действительно не нужно обрабатывать все ошибки и генерировать настраиваемое исключение. Помните, что ваш метод будет использоваться только другими разработчиками. Они должны уметь самостоятельно определять общие исключения фреймворка. ЭТО не стоит проблем.

7
ответ дан 27 November 2019 в 03:40
поделиться

Что ж, это зависит от вашей аудитории.

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

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

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

13
ответ дан 27 November 2019 в 03:40
поделиться

Ваш пример "до" отличается тем, что он ясен и краток.

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

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

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

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

Отсутствует документация по вашему методу. ; -)

У каждого метода есть определенные входные и выходные параметры и определенное результирующее поведение. В вашем случае что-то вроде: «Возвращает действительное открытое соединение в случае успеха, иначе возвращает null (или выдает исключение XXXException, как вам нравится). Помня об этом поведении, теперь вы можете решить, насколько защитным вы должны программировать.

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

  • Но, если вас просто интересует открытое соединение DBConnection или просто null (или какое-то определенное пользователем исключение) в случае сбоя, затем просто оберните все в try / catch и верните null (или какое-то исключение) в случае ошибки, а также объект else.

Так что я бы хотел сказать,

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

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

Что-то вроде этого (отредактировано в редакторе SO, поэтому синтаксис / компилятор не проверяет. Может не компилироваться ;-) )

private string GetConnectionString(String connectionName)
{

   //Get the connection string info from web.config
   ConnectionStringSettings cs= ConfigurationManager.ConnectionStrings[connectionName];

   //documented to return null if it couldn't be found
   if (cs == null)
       throw new ArgumentException("Could not find connection string \""+connectionName+"\"");
   return cs;
}

private DbProviderFactory GetFactory(String ProviderName)
{
   //Get the factory for the given provider (e.g. "System.Data.SqlClient")
   DbProviderFactory factory = DbProviderFactories.GetFactory(ProviderName);

   //Undefined behaviour if GetFactory couldn't find a provider.
   //Defensive test for null factory anyway
   if (factory == null)
      throw new Exception("Could not obtain factory for provider \""+ProviderName+"\"");
   return factory;
}

public DbConnection GetConnection(String connectionName)
{
   //Get the connection string info from web.config
   ConnectionStringSettings cs = GetConnectionString(connectionName);

   //Get the factory for the given provider (e.g. "System.Data.SqlClient")
   DbProviderFactory factory = GetFactory(cs.ProviderName);


   //Have the factory give us the right connection object
   DbConnection conn = factory.CreateConnection();

   //Undefined behaviour if CreateConnection failed
   //Defensive test for null connection anyway
   if (conn == null)
      throw new Exception("Could not obtain connection from factory");

   //Knowing the connection string, open the connection
   conn.ConnectionString = cs.ConnectionString;
   conn.Open()

   return conn;
}

PS: Это не полный рефакторинг, были выполнены только первые два блока.

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

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