Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Аспектно-Ориентированное Программирование (Aspect Oriented Programming, AOP)

Управление параллельными заданиями

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

  • Несогласованная обработка данных;
  • Предоставление неактуальных данных;
  • Разрушение данных из-за их рассогласования;
  • Взаимные блокировки (deadlocks);
  • Резкое снижение скоростных характеристик системы (в первую очередь пропускной способности системы и времени отклика).

 

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

 

Запрос (request) – соответствует отдельно взятому обращению к системе со стороны внешнего субъекта-клиента.

 

Сеанс (session) – это долговременный процесс взаимодействия клиента и сервера, включающий в себя один и более запросов.

 

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

  • Пессимистические блокировки (pessimistic locking);
  • Оптимистические блокировки (optimistic locking).

Оптимистичное блокирование даёт возможность обнаруживать конфликты, в то время как пессимистическое блокирование – предотвращать их.

 

Пессимистические блокировки делятся в свою очередь на разделяемые (shared) и монопольные (exclusive). Допускается одновременный захват разделяемых блокировок одной и той же порции данных несколькими процессами, в то время как монопольной блокировкой может обладать только один процесс. Причем если хоть один процесс обладает разделяемой блокировкой, получить монопольную блокировку уже никому не удастся до тех пор пока он не освободить разделяемую блокировку.

 

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

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

 

Транзакции (transaction) – это основной инструмент управления параллельными процессами в корпоративных приложениях. Транзакции в программных системах часто описывают в терминах свойств, обозначаемых аббревиатурой ACID:

  • Atomicity (атомарность). В контексте транзакции либо выполняются все действия из которых она состоит, либо ни одного.
  • Consistency (согласованность). Системные ресурсы должны пребывать в целостном и непротиворечивом состоянии как до начала транзакции, так и после её окончания.
  • Isolation (изолированность). Промежуточные результаты транзакции должны быть закрыты для доступа со стороны любой другой действующей транзакции до момента их фиксации.
  • Durability (устойчивость). Результат выполнения завершённой транзакции не должен быть утрачен ни при каких условиях.

 

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

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

  • Serializable (упорядочиваемые);
  • Repeatable-read (повторяемое чтение);
  • Read-committed (чтение фиксированных данных);
  • Read-uncommitted (чтение нефиксированных данных).

 

Уровень изоляции Чтение мусора (dirty read) Неповторяемое чтение Фантомные объекты
Read-uncommitted Да Да Да
Read-committed Нет Да Да
Repeatable read Нет Нет Да
Serializable Нет Нет Нет

 

Различают системные и бизнес транзакции. Системные транзакции поддерживаются непосредственно монитором транзакций, например СУБД, с то время как бизнес транзакции охватывают некоторую последовательность бизнес действий, которая должна обладать всеми свойствами транзакции (ACID), где в качестве ограничения целостности и непротиворечивости рассматриваются бизнес правила.

 

АСПЕКТ (от лат. 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 являются:

  • AspectJ (http://eclipse.org/aspectj/)
  • Aspekt Werkz (http://aspectwerkz.codehaus.org/)
  • Jboss AOP (http://www.jboss.com/products/aop)
  • Spring Framework (http://www.springframework.org/)

 

Для платформы.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’ов:

  • around – вызов перехваывается до выполнения целевого метода и Advice вправе сделать с вызовом всё что угодно, вплоть до полной его отмены и подмены целевого вызова, вызовом другого метода на другом объекте (например вызов удалённого метода)
  • before – вызов перехватывается до выполнения целевого метода и Advice может получть доступ ко всем параметрам вызова, например сделать валидацию, проверить права доступа или сделать запись в журнал
  • after – вызов перехватывается после выполнения целевого метода и Advice помимо параметров вызова получает доступ к возвращаемому значению
  • after throwing – вызов перехватывается в случае генерации исключения целевым методом
  • и другие

Тип 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

 

Constructor Dependency Injection позволяет не нарушать свойства инкапсуляции классов, т.е. требует, что бы класс, который инициализируется из контейнера предоставлял конструктор со всеми аргументами необходимыми для корректной инициализации класса. Это часто является не очень удобным, так как объекту может требоваться большое количество коллабораторов и, более того, некоторые из них могут являться опциональными. Это приводит к необходимости создания конструкторов с большим количеством аргументов, что затрудняет программирование.

 

Setter Dependency Injection предполагает установку каждого коллаборатора через открытое свойство (property или setter method), что несколько упрощает проектирование классов, так как нет необходимости заранее продумывать окончательную сигнатуру конструктора. Такой подход предполагает наличие у классов конструктора без параметров (default constructor). Минусом данного подхода является некоторое открытие внутренней структуры класса, что может оказаться нежелательным, так как с одной стороны нарушает инкапсуляцию – одно из основных свойств ООП, а с другой может привнести непосредственную угрозу с точки зрения безопасности и стабильности работы, так как подобным открытым setter методом, предназначенным только для инициализации может воспользоваться вредоносный код или просто кто-то по ошибке.

 

Отличительной чертой Spring Framework является то, что он позволяет применять оба подхода одновременно, выбирая тот или иной в зависимости от ситуации.

 

<== предыдущая лекция | следующая лекция ==>
Сеансы и состояния | Введение. Интеграция корпоративных приложений
Поделиться с друзьями:


Дата добавления: 2014-01-05; Просмотров: 994; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.108 сек.