В погоне за качеством кода: Безопасное программирование с помощью АОП (исходники)Источник: IBM developerWorks Россия Эндрю Гловер
Хотя защитное программирование надежно гарантирует состояние входных данных метода, его применение к целым сериям методов требует повторяющихся операций. В статье этого месяца Эндрю Гловер показывает простой способ добавления к коду многократно используемых проверочных ограничений с помощью АОП, контрактных спецификаций (design by contract) и полезной библиотеки под названием OVal Основной недостаток предварительного тестирования ПО заключается в том, что подавляющее большинство тестов представляют "безоблачные" сценарии. В таких ситуациях дефекты проявляются редко - проблемы, как правило, возникают именно в крайних случаях. Что такое крайний случай? Это ситуация, когда, например, передается значение В этом месяце автор представляет многогранный подход к устранению труднопредсказуемых дефектов в коде. Узнайте, что дает объединение принципов защитного программирования, контрактной спецификации и удобной общей среды проверки OVal. Выявление врагаКод в листинге 1 строит иерархию классов для объекта Листинг 1. Метод без проверки значения null
При написании этого кода я не заметил этого дефекта, но поскольку я - фанатик предварительного тестирования, то написал стандартный тест с помощью TestNG. Более того, я использовал удобную функцию Листинг 2. Тест TestNG для проверки двух значений
Дефект по-прежнему не обнаружен, но что-то в коде мне не нравится. Что если кто-то нечаянно передаст значение Проверить эту теорию довольно просто; не требуется даже начинать с нуля. Я просто добавляю Рисунок 1. Ужасный NullPointerException Здесь можно увидеть рисунок полностью.
Защитное программированиеПосле того как проблема обнаружена, следующий шаг заключается в выборе стратегии по ее устранению. Проблема состоит в невозможности контролировать тип вводимых данных, которые получает данный метод. Для решения проблем такого типа разработчики часто применяют методы защитного программирования, предназначенные для упреждающего обнаружения потенциальных ошибок. Классическая стратегия защитного программирования, предназначенная для обработки неопределенностей - это проверка объектов. Соответственно я добавил проверку равенства Листинг 3. Добавляем проверку на равенство null
Естественно, я также написал быстрый тест, чтобы удостовериться, что моя проверка действительно предотвращает Листинг 4. Подтверждение действенности проверки на null
В данном случае защитное программирование, похоже, выполняет свою роль. Но использование исключительно данной стратегии имеет свои издержки. Издержки защитного программирования
Хотя защитное программирование надежно гарантирует состояние входных данных метода, его применение к целым сериям методов требует повторяющихся операций. Знакомые с аспектно-ориентированным программированием, или АОП, узнают в этом сквозную задачу (crosscutting concern) , что означает, что методы защитного программирования простираются горизонтально по всему массиву кода. Эта семантика используется во многих различных объектах, но с чисто объектно-ориентированной точки зрения она не имеет ничего общего с самими объектами. Более того, концепция сквозных задач начинает просачиваться в понятие контрактных спецификаций программных компонентов (design by contract, DBC) . DBC - это технология, которая призвана гарантировать, что все компоненты в системе выполняют именно то, что они должны выполнять, за счет явного указания в интерфейсе каждого компонента его требуемой функциональности и ожиданий клиента. На языке DBC требуемая функциональность компонента называется постусловием и является, по существу, "обязательствами" компонента, а ожидания клиента собирательно называются предусловием . Более того, в терминах чистого DBC класс, действующий в соответствии с правилами DBC, имеет "контракт" с окружающим миром относительно поддержания им внутренней согласованности, известный как инвариант класса . Контрактные спецификацииПонятие DBC я ввел некоторое время назад в статье о программировании на Nice, объектно-ориентированном, совместимом с JRE языке программирования, разработанном с акцентом на модульность, выразительные возможности и безопасность. Интересно, что в Nice используются функциональные методы разработки, включая некоторые методы аспектно-ориентированного программирования. Среди прочего, функциональная разработка позволяет задавать предусловия и постусловия для методов. Хотя язык Nice поддерживает DBC, он существенно отличается от Java™ , поэтому его сложно внедрить в группах по разработке ПО. К счастью, имеются библиотеки для Java, облегчающие реализацию DBC. Каждая библиотека имеет свои "за" и "против", разные библиотеки могут использовать различные подходы к реализации DBC для Java; однако последние новинки в этой области позволяют использовать для реализации задач DBC средства АОП, действующие как "упаковки" методов. Предусловие проверяется перед выполнением "упакованного" метода, постусловие - после завершения выполнения метода. Одна из приятных особенностей использования АОП для создания конструкций DBC заключается в том, что сами конструкции можно отключить в средах, где DBC не требуется (подобно тому, как можно отключить операторы контроля). Однако настоящее изящество реализации принципов защитного программирования с помощью сквозных задач заключается в возможности эффективного многократного использования этих средств. Как известно, многократное использование является одним из основных принципов объектно-ориентированного программирования. Не правда ли, АОП здорово дополняет ООП? АОП с использованием OValOVal представляет собой общую среду проверки кода, поддерживающую простые конструкции DBC посредством АОП и позволяющую выполнять следующие операции:
Более того, OVal включает множество стандартных ограничений, что значительно упрощает процесс создания новых ограничений. Поскольку в OVal для определения элементов advices (рекомендаций) для понятий DBC используется реализация АОП AspectJ, в проект, использующий OVal, необходимо включить AspectJ. Хорошей новостью для тех, кто не знаком с АОП и AspectJ, является то, что требуется минимум усилий, а использование OVal (даже для создания новых ограничений) не потребует фактического кодирования аспектов, за исключением создания простого аспекта начальной загрузки, который подключает к коду аспекты, входящие в состав OVal. Перед созданием такого аспекта начальной загрузки необходимо загрузить AspectJ. В частности, необходимо включить JAR-файлы Начальная загрузка АОППосле загрузки AspectJ необходимо создать аспект, расширяющий Листинг 5. Аспект начальной загрузки DefaultGuardAspect
AspectJ включает задачу Ant под названием Следует помнить, что Листинг 6. Фрагмент файла сборки Ant с компиляцией АОП
Теперь, после того как мы настроили средства OVal и осуществили начальную загрузку процесса АОП, можно приступить к заданию простых ограничений для кода с использованием аннотирования Java 5. Многократно используемые ограничения OValЧтобы задать постусловия для метода с помощью OVal, необходимо аннотировать параметры метода. Соответственно, при вызове метода, аннотированного с ограничениями OVal, OVal проверяет ограничения до фактического выполнения метода. В нашем случае хотелось бы сделать так, чтобы метод Листинг 7. Ограничения OVal в действии
После того как мы задали это ограничение с помощью аннотаций, нам больше не требуется каждый раз добавлять в код условия для проверки на Разумеется, следующий шаг заключается в компиляции класса а Теперь модифицируем тест, представленный в листинге 4, чтобы убедиться, что вызов Листинг 8. Проверка исключения ConstraintsViolatedException
Определение постусловийКак видно, определение предусловий является довольно простой операцией. То же можно сказать и об определении постусловий. Например, если нам нужно, чтобы никакой из операторов, вызывающих
Разумеется, Другие ограничения OValOVal также поддерживает средства предварительной оценки членов классов до или после вызова методов. Преимущество этого механизма заключается в том, что он уменьшает потребность в многократных проверках ограничений, например, при проверке размеров коллекций или обсуждаемого ранее условия неравенства значению Например, в листинге 10 приведена задача Ant, создающая отчет для иерархии классов с помощью Листинг 10. Задача HierarchyBuilderTask с проверкой условия
Поскольку я использую OVal, я могу поступить так:
Эти два шага позволяют эффективно устранить проверку условий в методе Листинг 11. Измененная задача HierarchyBuilderTask без проверки условий
При каждом вызове Заключительные соображенияКонструкции защитного программирования предотвращают множество дефектов, но сами перегружают код повторяющейся логикой. Сочетание методов защитного программирования с аспектно-ориентированным программированием (посредством контрактных спецификаций) - это одна из возможностей сохранить сильные стороны защитного программирования без многократного повторения фрагментов кода. OVal - не единственная имеющаяся библиотека DBC; на самом деле конструкции DBC этой среды достаточно ограничены в сравнении с другими средами (например, в OVal нет простого способа задания инвариантов класса). С другой стороны, удобство использования OVal и широкий выбор ограничений делают эту среду удобным инструментом для простого добавления проверочных ограничений к коду. Кроме того, в OVal очень легко создавать собственные ограничения, поэтому призываю вас - хватит использовать проверочные условия, пользуйтесь возможностями АОП! |