понедельник, 26 января 2009 г.

АОП и Delphi(часть 2)

Теперь давайте кратенько, минут, эдак, на сорок, коснемся основных идей АОП. Вряд ли я смогу рассказать вам о них лучше учебника. Но такой задачи у меня и нет. Мне хотелось бы взглянуть на то, как АОП изменяет представление об архитектуре программы в голове программиста.

Так как АОП – это, по большому счету, дополнение к ООП, то и ключевых понятий в нем не так много. Нам понадобятся всего 4:
1) Аспект (Aspect) – это модуль, описывающий когда, в каком месте программы и какой код мы хотим вызывать. Говоря слово модуль, я придерживаюсь терминологии создателей АОП, хотя в нашем случае правильнее было бы сказать, что аспект – это некий класс, описывающий то, как надо модифицировать поведение другого класса. Поэтому аналогия с классом-хелпером в первой части этой статьи уместна, но не реализуема в полной мере.
2) Точка выполнения/присоединения (Joinpoint) - грубо говоря - любое место в программе, куда можно поставить breakpoint, хотя в конкретной реализации АОП и конкретном языке программирования это может быть далеко не так. Но для понимания такое определение нам хорошо подходит. В идеале получается, что мы можем сослаться на любое место в нашей программе, хотя на практике речь, конечно, идет о вызове методов, чтении свойств и т.д.
3) Срез (Pointcut) – это описание группы точек выполнения, отобранных по некоторым правилам. Например, может возникнуть задача трассировать вызовы всех публичных методов некоторого класса. Трудоемко и не эффективно описывать все эти методы в своем аспекте, а при добавлении или удалении методов снова модифицировать аспект. Поэтому, должны быть предусмотрены языковые конструкции, позволяющие описать сразу множество точек выполнения, связанных общими признаками. Можно даже реализовать некий язык запросов, упрощающий селекцию.
4) Применение (Advice) – это участок кода, которому можно передать управление до, после или быть может даже вместо выполнения некоторого кода, описанного некоторой точкой выполнения.

Таким образом, мы можем создать некий класс (аспект), который привносит дополнительную функциональность в уже работающий код. В этом классе описывается, в какое место или места (точка выполнения или срез) следует инжектировать код (применение), чтобы изменить поведение программы. И вот мы подошли к важному моменту, который многие статьи упоминают вскользь, сосредотачиваясь на практической стороне дела. А это, на мой взгляд, и является основной идеей: АОП – это, по сути дела возможность представить плоский код программы в виде нескольких функциональных блоков, выделив каждую ветвь бизнес-логики, разбросанную по плоской программе в отдельный законченный модуль. А уже модули разработчик может соединить в конкретное приложение с помощью аспектов. Иными словами, с помощью аспектов из готовых компонентов мы можем получить конкретную реализацию программы.
Теперь давайте посмотрим, какие преобразования происходят с исходным кодом при компиляции и сборке программы. К примеру, разработчик создает систему биллинга, включающую:
1) Модуль с четкой и лаконичной функцией оплаты клиентом некоторой услуги.
2) Модуль аутентификации и проверки прав доступа, описывающий, куда и как пользователю можно обращаться.
3) Модуль документирования действий, описывающий, когда и как логгировать действия пользователя.
4) Модуль, содержащий аспект, который определят, как использовать три предыдущих модуля(класса).
В данном случае основной бизнес-логикой программы является модуль номер 1. Именно он решает поставленную перед разработчиком задачу. Модули 2 и 3 – типичные примеры сквозной функциональности. Причем, если класс, отвечающий за безопасность – обязательный для работы программы, то лог может включаться только для тестовых версий. Изменить поведение программы не сложно - достаточно при компиляции использовать два разных аспекта – один с включенным логом, другой без. Прожженным программистам может показаться, что проще использовать директивы условной компиляции, плотно прописавшиеся в арсенале Delphi разработчика, но вот сторонники АОП пытаются доказать, что с приходом аспектов востребованность условной компиляции будет сведена к минимуму. Но об этом мы ещё поговорим чуть позже.
Важным игроком во всей этой истории является некий АОП-шный блендер, который перемешивает по описанным в аспекте правилам содержимое трех первых модулей, получая готовое к исполнению приложение. По сути дела, эта самая интересная техническая часть системы. Подходы к её реализации условно можно разделить на три группы:
1) Поддержка на уровне языка.
2) Препроцессорная обработка.
3) Использование внутренних механизмов компилятора и языка.
Понятно, что первый случай – идеальный вариант, который в данном конкретном случае пока мерещится в призрачном будущем. И хотя Ник Хождес в своем эссе прозрачно намекает на то, что формат DCU скоро канет в лету, а к нам придет новый супермодный компилятор, наивно полагать, что это творение сразу же можно будет использовать в реальных проектах. Пока оно созреет, пока обрастет кучей тредов в профильных конференция, покроется заплатами и сервис-паками, будет готова следующая версия Delphi, а может и не одна.
Вариант номер два. Используя препроцессор, мы получаем некий готовый исходный код, который потом отдается на откуп компилятору. В этом есть большой плюс, так как всегда можно посмотреть, что же там за лажа такая получилась на выходе. В случае с Delphi препроцессоров АОП мне найти не удалось, зато есть несколько решений по третьему пункту. В основном разработчики используют RTTI, хотя в сети есть и экзотические хакерские способы братьев-китайцев, смысл использования которых в собственных приложениях, мягко говоря, туманен.
Чтобы излишне не обнадеживать страждущих, сразу скажу, что готовые программы изменить под использование некоторых возможностей АОП вряд ли удастся, так как библиотеки используют свою иерархию классов, а справедливость и обоснованность предложенных решений не всегда очевидна. Тем не менее, библиотеки существуют, их можно тестировать и пробовать. А насколько они стабильны и удобны – может показать только ежедневное пристрастное использование.
Итак, MeAOP и Infra.

(продолжение следует)