Дьявол кроется в деталях.
-- Французская поговорка
Недавно, наткнулся на такое понятие, как "упреждающее программирование". Под этим термином авторы понимают достижение кодом малых целей: написание читаемого кода, проверка возвращаемых значений всех функций, применение шаблонов проектирования и т.д. А вот если код будет достигать этих малых целей, то в нем будут предотвращены многие проблемы, которые могут помешать достижению большой цели - выпуску продукта с минимальным количеством ошибок, в разумные сроки и с требуемым функционалом.
Немножко про упреждающее программирование будет подкатом.
На самом деле, все те рекомендации, которые можно объединить под термином упреждающее программирование достаточно тривиальны и, в большинстве случаев, сводиться к применению здравого смысла. Почему в большинстве случаев? Ну потому, что над некоторыми рекомендациями приходится задумываться достаточно серьезно, в чем же здесь смысл.
Вот, например, использование констант. Все ясно и логично. Посмотрите вот на этот код:
Согласитесь, все вписывается в требования здравого смысла... Теперь давайте посмотрим пример чуть посложнее. Взгляните, на вот этот фрагмент класса:
Кстати, если мы откроем большинство книжек по программированию, мы там увидим именно такой пример. И ведь никто из этих авторов не рассказывает, что в некоторых случаях, так делать нельзя. Можно получить существенные проблемы. Давайте, я приведу более правильный пример:
Это как раз тот случай, который не зная, объяснить здравым смыслом, достаточно тяжело, нужно знать особенности работы не только событий, но и многопоточных приложений. Дело в том, что во втором варианте, в случае многопоточной программы, может произойти проверка на неравенство null, а потом, у нашего потока отберут управление и передадут в тот поток, который отпишет от нашего события последний метод. Соответственно, когда нашему методу OnPrint вернут управление, в событии Print уже будет null, что вызовет Null Reference Exception.
Ну и в завершении этого примера, оригинальный вызов события, который я подсмотрел на Хабре (рекомендую, кстати, почитать по ссылке):
Ладно, на этом первый рассказ про упреждающее программирование я заканчиваю, но есть еще несколько концепций и примеров, которые я постараюсь изложить в других статьях.
-- Французская поговорка
Недавно, наткнулся на такое понятие, как "упреждающее программирование". Под этим термином авторы понимают достижение кодом малых целей: написание читаемого кода, проверка возвращаемых значений всех функций, применение шаблонов проектирования и т.д. А вот если код будет достигать этих малых целей, то в нем будут предотвращены многие проблемы, которые могут помешать достижению большой цели - выпуску продукта с минимальным количеством ошибок, в разумные сроки и с требуемым функционалом.
Немножко про упреждающее программирование будет подкатом.
На самом деле, все те рекомендации, которые можно объединить под термином упреждающее программирование достаточно тривиальны и, в большинстве случаев, сводиться к применению здравого смысла. Почему в большинстве случаев? Ну потому, что над некоторыми рекомендациями приходится задумываться достаточно серьезно, в чем же здесь смысл.
Вот, например, использование констант. Все ясно и логично. Посмотрите вот на этот код:
double price =
purchaseValue * 1.1 * 1.1;
За за что отвечают тут числа 1.1? И именно должно быть умножение или программист поленился возвести число в квадрат?
double price = purchaseValue * MarkUp * ServiceCharge;
Ведь, согласитесь, получается намного лучше? Во-первых, код стал прозрачнее, не закупочную цену умножают два раза на 1.1, а закупочную цену умножают на розничную наценку и наценку за обслуживание. А во-вторых, теперь, даже если одна из них изменится, никому не придет в голову нажать Ctrl+H и заменить во всем коде 1.1, на 1.2 (тем самым внеся существенную ошибку в расчет). Ведь достаточно найти константу и поправить ее значение.Согласитесь, все вписывается в требования здравого смысла... Теперь давайте посмотрим пример чуть посложнее. Взгляните, на вот этот фрагмент класса:
class EventExample
{
public event EventHandler Print;
protected void
OnPrint()
{
Print(this, new EventArgs());
}
...
}
Те, кто часто использует события, сразу мне скажут, что событие может быть null, поэтому необходимо добавить проверку Print на неравенство null, перед его вызовом. Например, вот так:
protected void
OnPrint()
{
if (Print != null)
{
Print(this, new EventArgs());
}
}
Кстати, если мы откроем большинство книжек по программированию, мы там увидим именно такой пример. И ведь никто из этих авторов не рассказывает, что в некоторых случаях, так делать нельзя. Можно получить существенные проблемы. Давайте, я приведу более правильный пример:
protected void
OnPrint()
{
EventHandler
handler = Print;
if (handler != null)
{
handler(this, new EventArgs());
}
}
Смотрите, появилась дополнительная переменная, дополнительное присвоение... А зачем оно все? Если знаете, то супер. А если нет, то рекомендую подумать, зачем такое может быть нужно. Ну а чтобы, с одной стороны вы подумали, а с другой, могли сравнить свои рассуждения с моими, я следующий абзац сделал белого цвета. Как подумаете, нажмите Ctrl+A и сравните ответы.Это как раз тот случай, который не зная, объяснить здравым смыслом, достаточно тяжело, нужно знать особенности работы не только событий, но и многопоточных приложений. Дело в том, что во втором варианте, в случае многопоточной программы, может произойти проверка на неравенство null, а потом, у нашего потока отберут управление и передадут в тот поток, который отпишет от нашего события последний метод. Соответственно, когда нашему методу OnPrint вернут управление, в событии Print уже будет null, что вызовет Null Reference Exception.
Ну и в завершении этого примера, оригинальный вызов события, который я подсмотрел на Хабре (рекомендую, кстати, почитать по ссылке):
public static class EventHandlerExtensions
{
public static void
SafeRaise(this EventHandler
handler, object sender, EventArgs e)
{
if
(handler != null)
{
handler(sender, e);
}
}
}
Как видите, появляется расширяющий метод, который можно использовать вот так:
protected void
OnPrint()
{
Print.SafeRaise(this, new EventArgs());
}
Метод стал значительно проще. И выработав привычку (соглашение по кодированию) использовать только SafeRaise для вызова событий, мы не только убережем себя от тривиальной ошибки с проверкой на null, но и от второй, более серьезной, которую даже воспроизвести будет значительно сложнее.Ладно, на этом первый рассказ про упреждающее программирование я заканчиваю, но есть еще несколько концепций и примеров, которые я постараюсь изложить в других статьях.
Комментариев нет:
Отправить комментарий