Автор: Sergey Teplyakov

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

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

clip_image002

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

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

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

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

Возвращение null object-а в случае возникновения исключений также является опасной практикой, поскольку вызывающий код просто не сможет определить, является ли пустой объект корректным значением, или же при его получении произошла ошибка:

publicSomeEntry ReadEntryById(int id)
{
try
{
// Читаем SomeEntry из базы данных
}
catch (Exception)
{
// Ядрёна кочарыжка! Как же вызывающему коду узнать,
// была ли ошибка, или записи с таким id нет в базе?
returnnull,
}
}

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

--------------

(*) За подробностями о том, что это за метафоры, обращайтесь к соответствующим заметкам: Технический долг и Эффект второй системы, соответственно.

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

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

Помогла статья? Оцените её!
0 из 5. Общее количество голосов - 0
 

You have no rights to post comments

Дмитрий Крикунов

Публикую статьи, обучающие курсы и новости по программированию: алгоритмам, языкам (С++, Java), параллельному программированию, паттернам и библиотекам (Qt, boost).