Читайте также:
|
|
АСПЕКТ (от лат. Aspectus — вид), точка зрения, с которой рассматривается какое-либо явление, понятие, перспектива. (Большой энциклопедический словарь)
Аспектно-ориентированное программирование (АОП) представляет собой одну из концепций программирования, которая является дальнейшим развитием процедурного и объектно-ориентированного программирования (ООП). Эта методология призвана снизить время, стоимость и сложность разработки ПО. В современном ПО, как привило, можно выделить определенные части, или аспекты, отвечающие за ту или иную функциональность, реализация которой рассредоточена по коду программы, но состоит из схожих кусков кода. АОП позволяет выделять куски кода отвечающие за схожую функциональность в обособленные модули, наподобие классов, называемые Аспектами, которые могу повторно переиспользоваться, тем самым снижая дублирование кода.
По оценкам специалистов, около 70% времени в проектах тратится на сопровождение и внесение изменений в готовый программный код. Поэтому в ближайшей перспективе роль АОП и подобных трансформационных подходов становится достаточно важной.
АОП предполагает наличие языковых средств, позволяющих выделять сквозную функциональность в отдельные модули. Это позволяет упрощать работу (отладку, модифицирование, документирование и т.д.) с компонентами программной системы и снижать сложность системы в целом. Здесь и далее под модулем (компонентом) понимается некоторая четко выраженная структурная единица программы — процедура, функция, метод, класс или пакет.
Как правило, любая программная система состоит из основной (предметно-ориентированной) и системной частей. Например, ядро системы обработки кредитных карт предназначено для работы с платежами, тогда как функциональность системного уровня предназначена для ведения журнала событий, целостности транзакций, авторизации, безопасности, производительности и т.д. Большинство подобных частей системы, известных как сквозная функциональность, затрагивает множество основных предметно-ориентированных модулей. Можно рассматривать сложную программную систему как комбинацию модулей, каждый из которых включает в себя, кроме бизнес-логики, часть сквозной функциональности из набора требований к системе.
Проблемы, связанные с использованием ООП:
Основные термины АОП:
JoinPoint — строго определенная точка выполнения программы, ассоциированная с контекстом выполнения (вызов метода, конструктора, доступ к полю класса, обработчик исключения, и т.д.).
Pointcut — набор (срез) точек JoinPoint удовлетворяющих заданному условию.
Advice — набор инструкций языка программирования, выполняемых до, после или вместо каждой из точек выполнения (JoinPoint), входящих в заданный срез (Pointcut).
Aspect — основная единица модульности в АОП. В аспектах задаются срезы точек выполнения (Pointcut) и инструкции, которые выполняются в точках выполнения (Advice).
Introduction (Mix-in) — способность аспекта изменять структуру класса путем добавления новых полей и методов, так и иерархию класса.
Pointcut и Advice определяют правила интеграции. Аспект — единица, напоминающая класс в ООП, она соединяет элементы pointcut и элементы advice вместе, и формирует модуль на срезе системы.
Weaving – процедура привязки Аспектов к загружаемым классам. Различается привязка на этапе компиляции (или посткомпиляции/постпроцессор) – offline weaving, и непосредственно в момент загрузки класса (class loader’ом) – так называемый on-line weaving.
Существует ряд продуктов, которые позволяют использовать АОП на практике. Наиболее распространёнными на сегоднящний день для платформы Java являются:
Для платформы.Net можно использовать как встроенный механизм Real и Transparent Proxy, которые позволяет прозрачно перехватывать создание контекстно-связанных объектов (унаследованных от класса ContextBoundObject) или использовать Spring Framework.Net (http://www.springframework.net/), который предоставляет функциональность аналогичную той что предоставляет Spring Framework для Java.
Пример. Пусть есть несколько классов, представляющих работу банкомата и предоставляющих две операции: снятие денег со счёта клиента и запрос остатка.
Допустим необходимо добавить аспект, который бы записывал в лог все попытки запуска операции снятия денег со счёта, при этом сделать так, чтобы код, отвечающий за запись в лог должен быть отделён от бизнес логики.
Для этого воспользуемся каркасом (Framework) Aspect Werkz, которые позволяет задавать аспекты в виде обычных Java-классов и подключать ьих с помощью XML-файла, в котором описываются JointPoint’ы.
<aspectwerkz>
<system id=”AspectWerkzExample”>
<package name=”atm”>
<aspect class=”aspect.AtmLogAspect”>
<pointcut name=”operationPerformance” expression=”execution(* atm.domain.Atm.withdraw(..))” />
<advice name=”logOperationBefore” type=”before” bind-to=”operationPerformance”/>
<advice name=”logOperationAfter” type=”after” bind-to=”operationPerformance”/>
</aspect>
</package>
</system>
</aspectwerkz>
Данный файл, говорит о том, что выполнение любого метода Withdraw() с любыми параметрами на экземпляре класса Atm будет перехвачено и управление передано классу AtmLogAspect методу logOperationBefore и logOperationAfter соответственно до и после вызова указанного метода.
Указанные методы реализованы в классе Аспекта следующим образом:
public class AtmLogAspect {
public void logOperationBefore(JoinPoint joinPoint){
Atm atm = (Atm)joinPoint.getRtti().getTarget();
String methodName = ((MethodRtti)joinPoint.getRtti()).getName();
int amount = (Integer)((MethodRtti)joinPoint.getRtti()).getParameterValues()[0];
Client client = atm.getClient();
System.out.println(“client ‘” + client.getName() + “’ is trying to perform an operation “ + methodName + “(“ + amount + “)”);
}
public void logOperationAfter(JoinPoint joinPoint){
Atm atm = (Atm)joinPoint.getRtti().getTarget();
String methodName = ((MethodRtti)joinPoint.getRtti()).getName();
трибут result = (Boolean)((MethodRtti)joinPoint.getRtti()).getReturnValue();
Client client = atm.getClient();
System.out.println(“client ‘” + client.getName() + “’ has performed an operation “ + methodName + “ with “ + result + “ result”);
}
}
Данные методы используют набор интерфейсов, определённый в каркасе Aspect Werkz для доступа к контексту исполнения, т.е. позволяют получать параметры перехваченного вызова и, таким образом, влиять на выполнение вызова. Например можно вставить проверку прав доступа и генерировать Exception, тем самым прерывая вызов метода. Можно вообще вместо целевого метода на целевом объекте вызывать метод на каком либо другом объекте, тем самым реалиуя семантику прокси-объекта – например выполняь удалённый вызов процедур.
В Aspect Werkz (и вообще в других АОП каркасах) доступны следующие типы Advice’ов:
Тип Advice’а указывается в XML файле.
Для задания JointPoint’ов в Aspekt Werkz используется специальный язык шаблонов (Pattern Language).
15. Inversion of Control (IoC) / Dependency Injection
В ООП важным моментом при проектировании классов и систем состоящих из них является проектирование классов как можно более независимыми друг от друга (low-coupling). Положительным следствием такого принципа является относительная лёгкость взаимозамены одной реализации класса другой, при условии что оба класса реализуют общий интерфейс. Этим же продиктована и рекомендация проектирования классов и их взаимодействий на основании интерфейсов. Т.е. при разработке рекомендуется избегать прямого обращения к конкретной реализации класса и использовать вместо этого общий интерфейс. Например при использовании коллекций в Java, вместо конкретных реализаций рекомендуется использовать интерфейсы, так вместо прямого обращения к ArrayList рекомендуется использовать интерфейс List или ещё лучше – интерфейс Collection. Это позволит развязать зависимые реализации классов и подставить при необходимости, например вместо ArrayList LinkedList.
Пусть имеется некий простой класс, который использует коллекцию.
Public ClassA{
private Collection values = new ArrayList();
public businessMethod(Object value){
values.add(value);
}
}
При необходимости можно легко подменить реализацию интерфейса Collection. Однако, для того, что бы подменить реализацию придётся менять сам целевой класс (т.е. вместо values = new ArrayList(); написать values = new LinkedList();) и перекомпилировать его.
Альтернативным подходом является выделение функциональности по заданию конкретной реализации вовне. Например можно спроектировать класс нижеследующим образом.
Public ClassA{
private Collection values;
public void setValues(Collection values){
this.values = values;
}
public businessMethod(Object value){
values.add(value);
}
}
Перед использованием класса, ему необходимо задать пустую коллекцию, которая будет использована в бизнес методах. Как видно такая реализация полностью абстрагирует класс от конкретной реализации интерфейса Collection. Обязанность за правильную инициализацию класса берёт на себя некий внешний класс, называемый Assembler. Его задачей является правильное конфигурирование и подготовка к работе экземпляров бизнес классов. Assembler чем-то напоминает известный шаблон Builder или Factory, однако является более общим и позволяет не только примитивно инициализировать класс, но и строить целый граф объектов. Пример простого класса Assembler приведён ниже.
Public class ClassAAssebmlerImpl{
public ClassA build(){
ClassA classA = new ClassA();
classA.setValues(new ArrayList());
return classA;
}
}
Определения:
Collaborator – объект или компонент, участвующий в структурных связях и взаимодействиях с целевым объектом (под целевым объектом в данном контексте понимается рассматриваемый класс или объект).
Dependency Injection – техника внедрения объектов Collaborator’ов в целевые объекты извне, т.е. при помощи дополнительных классов, выступающих в роли Assembler’ов.
Dependency Injection позволяет избавиться от необходимости жёстко кодировать реализацию конкретного интерфейса непосредственно в целевом классе.
Альтернативным подходом является Service Locator. Она будет рассмотрена ниже.
Существует некоторое количество универсальных реализаций Assembler’а, которые позволяют использовать конфигурационные файлы для задания объектов и их коллабораторов, например в формате XML. Это избавляет от необходимости создания большого количества инфраструктурного кода и позволяет ещё больше снизить взаимосвязанность (low-coupling) объектов между собой. Такая инфраструктура часто называется контейнером объектов (container). Наиболее известным контейнером на настоящий момент для платформ Java и.Net является Spring Framework. Этот контейнер позволяет задавать очень сложные графы объектов в виде XML файла (или группы связанных файлов, что позволяет упростить разработку несколькими людми одновременно), задавать коллабораторы для объектов и значения их полей (в случае простых типов, таки как числа, строки, даты и т.п.) Помимо этого Spring Framework позволяет автоматически оборачивать объекты Proxy-объектами, использовать техники AOP (Aspect Oriented Programming). Более того, Spring предоставляет широкий набор уже реализованных (out-of-the-box) компонент и сервисов, таких как сервисы передачи сообщений (messaging), сервисы для работы с транзакциями, соединениями с БД, таймеры и т.п. Основной идеей Spring Framework является, так называемый, non-intrusive подход, который постулирует минимальную зависимость бизнес компонент и классов системы от контейнера, в данном случае от Spring Framework. Т.е. классам системы нет необходимости наследовать какие-либо специальные интерфейсы, как, например, было в случае с EJB до версии 3.0.
Выделяют два вида Dependency Injection:
Constructor Dependency Injection позволяет не нарушать свойства инкапсуляции классов, т.е. требует, что бы класс, который инициализируется из контейнера предоставлял конструктор со всеми аргументами необходимыми для корректной инициализации класса. Это часто является не очень удобным, так как объекту может требоваться большое количество коллабораторов и, более того, некоторые из них могут являться опциональными. Это приводит к необходимости создания конструкторов с большим количеством аргументов, что затрудняет программирование.
Setter Dependency Injection предполагает установку каждого коллаборатора через открытое свойство (property или setter method), что несколько упрощает проектирование классов, так как нет необходимости заранее продумывать окончательную сигнатуру конструктора. Такой подход предполагает наличие у классов конструктора без параметров (default constructor). Минусом данного подхода является некоторое открытие внутренней структуры класса, что может оказаться нежелательным, так как с одной стороны нарушает инкапсуляцию – одно из основных свойств ООП, а с другой может привнести непосредственную угрозу с точки зрения безопасности и стабильности работы, так как подобным открытым setter методом, предназначенным только для инициализации может воспользоваться вредоносный код или просто кто-то по ошибке.
Отличительной чертой Spring Framework является то, что он позволяет применять оба подхода одновременно, выбирая тот или иной в зависимости от ситуации.
Дата добавления: 2015-10-24; просмотров: 195 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Структурные шаблоны | | | Подходы к межсистемной интеграции |