Научная статья на тему 'Стратегия управления исключениями в корпоративных приложениях'

Стратегия управления исключениями в корпоративных приложениях Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
290
20
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
ИСКЛЮЧЕНИЕ / EXCEPTION / ОБРАБОТКА ИСКЛЮЧЕНИЙ / EXCEPTION HANDLING / СОГЛАСОВАННОЕ СОСТОЯНИЕ / CONSISTENT STATE / ГАРАНТИИ ИСКЛЮЧЕНИЙ / ПРИНЦИП САМУРАЯ / SAMURAI PRINCIPLE / ЦЕПОЧКА ОБЯЗАННОСТЕЙ / CHAIN OF RESPONSIBILITY / ИЕРАРХИЯ КЛАССОВ / HIERARCHY OF CLASSES / ПОЛИМОРФИЗМ / POLYMORPHISM / МОДУЛЬНОЕ ТЕСТИРОВАНИЕ / UNIT TESTING / EXCEPTION GUARANTEES

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Кожевников Д.О.

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

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Кожевников Д.О.

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Discusses practices and patterns of management by errors in a code of the enterprise applications, applicable with object-oriented design patterns and aimed at increase the stability of work and data security, and also on simplification of process of testing of a code of applications.

Текст научной работы на тему «Стратегия управления исключениями в корпоративных приложениях»

УДК 004.056.2: 004.054

стратегия управления исключениями в корпоративных

приложениях Д.О. Кожевников

ФГБОУ ВПО «Сибирский государственный технологический университет», 660049, Красноярск, пр. Мира, 82, е-mail: d.o.kozhevikov@gmail.com

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

Ключевые слова: исключение, обработка исключений, согласованное состояние, гарантии исключений, принцип самурая, цепочка обязанностей, иерархия классов, полиморфизм, модульное тестирование

Discusses practices and patterns of management by errors in a code of the enterprise applications, applicable with object-oriented design patterns and aimed at increase the stability of work and data security, and also on simplification of process of testing of a code of applications.

Key words: exception, exception handling, consistent state, exception guarantees, samurai principle, chain of responsibility, hierarchy of classes, polymorphism, unit testing

ВВЕДЕНИЕ

Обработка ошибок и механизмы для её поддержки являются неотъемлемой частью проектирования, разработки и тестирования современных корпоративных приложений (Фаулер, 2012). Ошибки, возникающие в процессе работы приложения, способны не только обрушить текущий сеанс работы, но и внести несогласованность в данные пользователей. В случаях, когда под управлением приложения находятся хранилище наборов критически важной информации с хрупкими взаимосвязями, необработанная ошибка может стоить очень дорого. Персональные, финансовые или технические данные, являющиеся основой OLTP или OLAP системы, всегда должны находиться в согласованном состоянии. Застраховаться от потери информации позволяет резервное копирование, однако от внесения несогласованных изменений застраховаться можно только на уровне прикладных приложений.

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

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

Таким образом, ответственность за обработку ошибок в целом нельзя возложить на определённый класс или подсистему, так как она должна быть распределена по всему приложению. Такое положение дел порождает ряд сложностей в выработке концепции обработки ошибок в объектно-ориентированном коде приложения. К ним относится: идентификация и сбор необходимой информации об ошибке, определение достаточности этой информации, реализация механизма передачи информации от нижележащих подсистем к вышележащим, определение границ ответственности отдельных классов за проверку результатов работы других классов и сторонних компонентов и т.п. Здесь следует отметить тот факт, что в противоположность обычному потоку вызовов, который направлен от вышележащих слоёв к нижележащим, вызовы обработки ошибок может быть направлено в обратном направлении.

Актуальность и насущность проблемы обработки ошибок подтверждается хотя бы тем, что современные объектно-ориентированные языки программирования предоставляют соответствующие механизмы на уровне языковых конструкций. Доминирующим механизмом является обработка исключительных ситуаций или исключений (Макконнелл, 2008). Дан-

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

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

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

ГРАНИЦЫ ОТВЕТСТВЕННОСТИ

КЛАССОВ

Основополагающим паттерном проектирования, описывающим процесс обработки исключений, является Цепочка обязанностей (Chain of responsibility, Гамма, 2012 ). Данный структурный паттерн определяет отношения в композиции объектов, передающих запрос или сообщение по цепочке. Таким образом, возможно избежать жёсткой связи между объектом клиентом, который послал запрос, и его конечным получателем и при этом обработать запрос несколько раз в цепочке в меру ответственности каждого обработчика. Каждый объект в цепочке является обработчиком и может выполнить код обработки, дополнить или подменить запрос и передать его далее по цепочке.

Особенностью использования паттерна Цепочка обязанностей при обработке исключений является то,

что механизмы передачи сообщений от клиента к обработчику и между обработчиками уже реализованы конструкциями языка. А сама цепочка обработчиков есть не что иное, как последовательность вызовов методов. Клиентом является метод, возбуждающий исключение, а обработчиками являются блоки обработки исключений методов в последовательности вызовов. Последовательность вызовов в свою очередь обусловлена композицией классов приложения и сценариями их взаимодействия.

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

Целью формирования единой стратегии управления исключениями в рамках всего приложения является нейтрализация данного фактора, то есть снижение отягощающего влияния, которое оказывает код обработки исключений на код класса и подсистемы в целом. Здесь можно выделить две категории мер, которые могут войти в состав стратегии управления исключениями: соглашения и программные решения. Соглашения должны формировать общую концепцию обработки и определять единые правила проектирования классов исключений, трансляции и оборачивания объектов исключений, распределения ответственности за обработку между классами подсистемы или слоя. Соглашения обязательны для всех классов в приложения, а соответственно их должны соблюдать все разработчики, принимающие участие в проекте. К программным решениям относятся те программные инструменты, помимо базовых механизмов языка, которые разработчики используют для облегчения работы с исключениями. Например, аспектно-ориентированные обработчики.

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

Одними из самых известных соглашений в обработке исключений является гарантии исключений Дэйва Абрахамса, которые были разработаны и популяризированы для языка С++. В современной интерпретации три гарантии формулируются следующим образом:

1) базовая гарантия - при возникновении исключения в любом методе, программа должна вернуться в согласованное состояние;

2) строгая гарантия - если при выполнении операции возникает исключение, то это не должно оказать какого-либо влияния на состояние приложения;

3) гарантия отсутствия исключений - метод никогда не генерирует исключения.

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

Базовой метафорой, которая определяет ответственность метода в отношении ошибок и исключений, принимается принцип самурая (Samurai Principle), также известный как DoOrDie или WithYourShieldOrOnIt принципы. Принцип гласит, что открытый метод любого класса обязан выполнить возложенную него функцию и вернуть корректный результат, а если по каким-либо причинам этого сделать не удаётся, то метод должен сгенерировать исключение. В то же время метод может вообще не приниматься за работу, если ему были переданы некорректные аргументы. Метафора основана на аналогии с кодексом чести самурая. Самурай не будет браться за задание, которое противоречит его моральным принципам и кодексу чести. Но если самурай взялся за выполнение задания, он выполнит его или умрёт. К формулировке принципу также иногда добавляют, что самурай, прежде чем умереть, обязан известить своего того, кто дал задание, о причинах неудачи. Из этого следует, что метод обязан снабдить генерируемое исключение всей необходимой информацией об ошибке.

Принимаемые соглашения обязывают разработчика добавить в обязанности каждого открытого метода каждого класса проверку корректности своих аргументов. Метод не может надеяться на то, что вызывающий код позаботился о корректности передаваемых данных. Тем более метод не может надеяться на то, что классы, которым он делегирует часть операций с аргументами, правильно отреагируют на непроверенные данные. Стоит, однако, отметить, что проверка корректности аргументов должна быть ограничена абстракцией класса и его долей ответственности. Этот вывод очень важен не только в контексте управления ошибками, но для задачи распределения ответственности между классами в целом.

Из перечисленных соглашений следует ряд важных выводов, которые также можно сформулировать в виду соглашений:

1) исключения и блоки для их обработки не должны использоваться в качестве управляющих конструкций. Управление нормальным потоком выполнения является плохой практикой проектирования, так как подразумевает безусловные переходы меж-

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

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

3) метод не должен возвращать неопределённое значение, если невозможно вернуть корректное. Метод, возвращающий неопределённое значение, вводит в заблуждение вызывающий код и нарушает принцип самурая, так как неопределённое значение не является корректным, следовательно, необходим, сообщить об этом клиенту явным образом. Проверка корректности возвращаемых значений может существенно засорить код клиента и скрыть его реальное назначение. Поэтому каждый публичный метод должен гарантировать возврат корректного значения, либо генерировать исключения. Тем не менее, не во всех случаях генерация исключения будет релевантным решением, однако значения ссылки null недопустимо в любом случае. Хорошее решение предлагает паттерн NullObject (Фаулер, 2011).

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

ПРОЕКТИРОВАНИЕ КЛАССОВ

ИСКЛЮЧЕНИЙ

Прежде чем обратиться к теме проектирования классов исключений, нужно сказать о том, откуда проистекает потребность в этих классах. Современные языки и их библиотеки предоставляют разработчику широкий набор классов исключений, описывающих базовые исключительные ситуации: обращение по null-ссылке, выход индекса за границы массива, деление на ноль и т.д. Разработчик может генерировать исключения библиотечных классов и использовать их во всем приложении, не создавая своих собственных классов. Однако существующие классы невозможно расширить для хранения дополнительной, специфической информации. Более того, некорректно использовать готовый класс исключений для идентификации логической ошибки в работе программы, которая на самом деле связана

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

Таким образом, собственные классы исключений в первую очередь необходимы для более точной идентификации ошибок возникающих в клиентском коде. Более того, даже исключения, генерируемые кодом платформы, должны быть как можно раньше обёрнуты исключениями собственного типа, чтобы точно идентифицировать место возникновения ошибки. Библиотечные классы должны использоваться в блоках перехвата исключений для идентификации ошибок, возникающих в коде сторонних компонентов. Распространения исключений библиотечных классов по цепочке обязанностей следует избегать. Ещё одним их применением является создание тестовых объектов-заглушек, для тестовых сценариев, когда необходимо сымитировать некоторую стандартную ошибку стороннего кода и проверить реакцию клиентской программы.

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

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

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

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

Выделение нового класса и добавление его в иерархию может происходить по двум причинам:

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

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

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

ЖИЗНЕННЫй ЦИКЛ ИСКЛЮЧЕНИЯ

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

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

ся исключением собственного класса. По сути имеет место применение паттерна Декоратор (Гамма Э., 2012). Для того, чтобы как можно точнее идентифицировать ошибку внешней библиотеки, необходимо перехватывать наиболее конкретный тип исключений. Во втором случае, возбуждаемое исключение должно содержать полную информацию о том, как, кем и при каких условиях бизнес-правило было нарушено. В обоих случаях информация об ошибке должна быть в первую очередь запечатлена в имени класса исключений. Так как для каждой исключительной ситуации проектируется отдельный класс, но код генерации нового исключения краток и хорошо читается;

2) перехват - методы типов вышележащих слоёв должны иметь возможность перехватывать исключения типов из нижележащих слоёв. Паттерн Цепочка обязанностей предполагает, что каждый обработчик в цепочке совершает некоторые (лежащие в сфере его ответственности) действия по полученному запросу прежде чем передать его далее. Часто необходимо добавлять дополнительную информацию в экземпляр исключения в нескольких методах последовательности вызовов;

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

4) обработка - меры, которые предпринимает программа, чтобы разрешить исключительную ситуацию и вернуться в согласованное состояние. Часто обработка ограничивается только отображением предупреждающего сообщения пользователю. Однако, обработка может включать и более сложные сценарии. Лучше всего делегировать обязанности по выполнению любых сценариев обработки на специализированные типы. Таким образом, можно одновременно не нагружать публичные классы лишней ответственностью, и реализовать общие сценарии обработки для всего приложения в одном месте. Примерами такой функциональности могут послужить логирование, откат транзакции, отправка отчётов разработчикам и т.п.

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

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

Единая стратегия управления исключениями в приложения не должна отрицательным образом сказываться на такой характеристике открытых методов классов как тестируемость. В идеале всякий открытый метод должен быть снабжён набором модульных тестов, которые отрабатывают сценарии работы метода в различных условиях, в том числе и ошибочные сценарии, заканчивающиеся генерацией исключения. Удобство написания тестовых сценариев зависит от изолированности кода класса, то есть количества и характера его зависимостей (Хант, 2012). Явное использование класса исключения при его генерации в коде метода - это тоже зависимость. Поэтому классы исключений должны быть максимально просты и не иметь сложных зависимостей, обращений к внешним ресурсам и статическим полям. В противном случае для тестирования метода придётся моделировать существенно более сложное окружение, и модульные тесты станут больше похожи на функциональные.

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

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

ЗАКЛЮЧЕНИЕ

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

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

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

1) поддерживать гарантии безопасности исключений, обеспечивая возвращение приложения в согласованное состояние при любых возникающих ошибках и атомарность критичных операций;

2) следовать принципу самурая при проектировании открытых методов классов. В ответственность метода попадает проверка корректности аргументов и гарантия возвращения корректного значения. В противном случае метод должен генерировать исключение;

Проектирование собственных классов исключение регламентируется предлагаемой стратегией следующим образом:

1) необходимо создавать один базовый класс исключений для всей иерархии собственных классов приложении для более точной идентификации причины возникшей ошибки и отделения ожидаемых ошибок от непредвиденных;

2) всякий слой или подсистема может иметь свой базовый класс исключений, наследующий классу на вершине иерархии для использования преимуществ полиморфных блоков перехвата и создания сложных сценариев обработки;

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

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

5) класс исключений не должен иметь сложных внешних зависимостей, обращений к ресурсам или статическим полям, чтобы избежать усложнения тестовых сценариев при модульном тестировании;

Жизненный цикл исключения в предлагаемой стратегии регламентируется следующими соглашениями:

1) объект исключение обрабатывается в цепочке

обязанностей, составленной из блоков обработки исключений методов в последовательности вызовов;

2) исключения библиотечных типов используются только в блоках перехвата тех методов, которые работают со сторонними компонентами. При этом необходимо перехватывать наиболее конкретный тип исключений с тем, чтобы точнее идентифицировать ошибку;

3) для распространения информации об ошибке, возникшей в коде стороннего компонента, необходимо использовать исключения собственных классов в качестве декораторов. Таким образом, ошибка будет точнее идентифицирована по месту, а специфические детали стороннего компонента будут скрыты от остальной части программы;

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

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

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

Макконнелл, С. Совершенный код. Мастер класс. Практическое руководство по разработке программного обеспечения. (Текст) / С. Макконнелл. - СПб.: Питер, 2008. - 896 с.

Гамма, Э. Приёмы объектно-ориентированного проектирования. Паттерны проектирования. (Текст) / Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес. - СПб.: Питер, 2012.

- 368 с.

Фаулер, М. Шаблоны корпоративных приложений. (Текст)/

М. Фаулер. - М.: Вильямс, 2011. - 544 с. Мартин Р.С. Принципы, паттерны и методики гибкой разработки на языке С#. (Текст) / Р.С. Фаулер. - СПб.: Символ, 2012. - 768 с. Хант, Э. Программист-прагматик. Путь от подмастерья к мастеру. (Текст)/ Э. Хант, Д. Томас. - М.: Лори, 2012.

- 270 с.

Поступила в редакцию 01 ноября 2012 г. Принято к печати 07 декабря 2012 г.

i Надоели баннеры? Вы всегда можете отключить рекламу.