Аспектно-ориентированное программирование Copyright © Мигинский Д.С.,
Чрезмерное усложнение дизайна для реализации принципа модульности или его нарушение при реализации ряда требований Например: журналирование событий (для отладки и т.д.) персистентность транзакционность информационная безопасность обработка ошибок Предпосылки для появления АОП
Cross-cutting concern Сквозная функциональность – функциональность, затрагивающая существенную часть модулей системы Сквозная функциональность не может быть выделена в отдельный модуль без существенного усложнения дизайна
Недостатки ООП/ООД Существенное влияние ограничений и условностей языков программирования (в первую очередь C++ и Java) Ориентированность ООД на простейшую реализацию объектной модели Невозможность во многих случаях соблюдения собственных принципов Как следствие – необходимость использования большого количества точечных решений, как например GOF-patterns
Распространенные техники Шаблоны проектирования Метаописания (напр. Hibernate, Eclipse RCP, WSDL) Аннотации (Java, C#) Интроспекция
Задача: наблюдение за изменением данных Постановка При внешних изменениях документа должна обновляться его визуализация Проблема: циклическая зависимость
Решение 1: Observer Проблемы: усложнение дизайна документ получает несвойственную для него функциональность (нарушение One-Responsibility Rule)
Язык AspectJ Проект AspectJ Руководство по языку AspectJ Development Tools for Eclipse
Основные понятия AOP (AspectJ) Join point (точка выполнения) – определенная идентифицируемая точка выполнения программы Pointcut (срез) – набор (класс) точек выполнения программы Advice – изменение функциональности, применяемое к точке выполнения Introduction – дополнительное внешнее свойство уже существующего класса Aspect (аспект) – организационная сущность для всего вышеперечисленного
Другие встречающиеся термины Wrapping Weaving Intercepting …
Задача: «внешний» рефакторинг кода Проблемы: нарушение инкапсуляции в Rectangle неконтролируемые изменения Rectangle отсутствие контроля ошибок Предположения: Rectangle инстанциируется только в RectangleFactory Client не должен модифицировать Rectangle Размеры прямоугольника неотрицательны
АОП-решение public aspect RectangleConstraints { pointcut rcSetter (double newval) : set (double Rectangle.*) && args (newval); void around (double newval) : rcSetter (newval){ if (newval >= 0) proceed (newval); else proceed (0); } pointcut wrongInstance () : call (Rectangle.new(..)) && !within (RectangleFactory); declare warning: wrongInstance (): "Incorrect instantiation context for Rectangle"; declare warning: set (double Rectangle.*) && !within (RectangleFactory): "Incorrect modification context for Rectangle"; }
set/get pointcuts //перехват присваивания любому полю типа int любого класса //не рекомендуется к использованию pointcut setAnyInt1 (): set (int *); //перехват присваивания любому полю типа int в классе Class1 //с возможностью использования аргумента pointcut setAnyInt2 (int val): set (int Class1.*) && args (val); //--//-- для любого класса, имя которого начинается с Class и //имя аргумента начинается с f pointcut setAnyInt3 (int val): set (int Class*.f*) && args (val); //перехват использования поля в правой части pointcut getAnyInt1 (int val): get (int Class*.f*) && args (val);
call/execute pointcuts //перехват вызова метода любого класса, начинающегося с set, //с произвольным списком аргументов и возвращаемым значением //не рекомендуется к использованию pointcut setterAny (): call (* *.set* (..)); //перехват вызова метода любого класса, начинающегося с set, //с произвольным возвращаемым значением и аргументом типа int //не рекомендуется к использованию pointcut setterAnyInt (int val): call (* *.set* (int)) && args (val); //перехват вызова set* вне контекста класса ValueManager pointcut setAnyInt2 (int val): call (* *.set* (int)) && args (val) && !within (ValueManager);
Context pointcuts within (Type) – внутри определенного класса whithincode (Method) – внутри определенного метода this (Type || id) – вызывающий объект заданного типа target (Type || id) – объект вызываемого метода заданного типа args (Type[] || id[]) – аргументы определенного типа
Разновидности pointcut вызов метода доступ к атрибуту (get/set) генерация/перехват исключения инициализация класса/объекта контекст (в определенном классе или методе) …
Разновидности advice before – до after – после (выполнения тела метода, возврата значения, генерации исключения) around - вместо
Тело advice advice по сути является неявно вызываемым методом, тело advice аналогично телу любого метода + ряд дополнительных возможностей: thisJoinPoint – объект, описывающий контекст вызова (join point) proceed ( ) – вызов исходной join point (применительно к around)
inter-type declarations //определение ошибок и предупреждений времени компиляции declare warning: : Message declare error: : Message //определение суперклассов/интерфейсов declare parents: Document implements Serializable declare parents: Document extends DataqObject //определение/реализация методов public boolean Document.isEmpty () {…} //определение атрибутов private String Document.name = Empty
Технические возможности AspectJ точечная модификация императивной части кода модификация определений классов: определение новых методов, атрибутов, суперклассов и интерфейсов контекстный полиморфизм управление компиляцией
Задача: разграничение доступа
Решение 1 Недостаток: необходимо модифицировать код Service
Решение 2 Недостаток: авторизуются только запросы клиента, но не внутренние сообщения Service
Решение 3 (АОП)
«Принципы» неиспользования АОП АОП – это НЕ средство взлома чужого кода АОП – это НЕ способ создания неуправляемого кода (см. также «самомодифицирующийся код», «блюдо спагетти») АОП – это НЕ то средство, которое необходимо применять всегда и везде
Генерализация аспектов: пример использования
Применение генерализации аспектов: способ 1 Базовый (абстрактный) аспект определяет набор pointcuts, как точек расширения системы Аспект-наследник определяет поведение в терминах advices
Применение генерализации аспектов: способ 2 Базовый (абстрактный) аспект определяет набор абстрактных pointcuts, а также некоторую функциональность в терминах advices Аспект-наследник связывает функциональность с кодом путем определения pointcuts
Observer: реализация в AspectJ Запустите Eclipse Создайте новый проект File->New->Project->AspectJ-> AspectJ Examples->Observer Example Разберите код и объясните его
Задача Реализуйте по аналогии с примером Observer универсальный абстрактный аспект, который обеспечивал бы функциональность undo/redo. Привязка к конкретным классам должна осуществляться за счет аспектов-наследников. Продемонстрируйте работу аспекта на примере собственной реализации класса, представляющего двумерную точку (либо любого другого класса данных). Класс должен реализовать только функции бизнес- логики (аксессоры к атрибутам).