4
УДК 004.4'2
Д.Ю. Подорожкин, А.Р. Когай, В.О. Сафонов
ПРИМЕНЕНИЕ МЕТОДОВ АСПЕКТНО-ОРИЕНТИРОВАННОГО ПРОГРАММИРОВАНИЯ ПРИ РАЗРАБОТКЕ ПРОГРАММНЫХ СИСТЕМ
Аспектно-ориентированное программирование (АОП) [1, 2] - перспективный подход к разработке программ, предлагающий средства для модуляризации сквозной функциональности и ее автоматизированного добавления в целевую программу. АОП расширяет традиционную концепцию модуля по Г. Майерсу [3] понятием аспект (aspect). Аспект - это специальный модуль, содержащий фрагменты кода сквозной функциональности, активируемые в заданных точках целевой программы.
Aspect.NET [4] - языково-независимая среда разработки АОП-приложений для платформы Microsoft.NET. Aspect.NET является расширением (add-in) популярной интегрированной среды разработки Microsoft Visual Studio. Это означает, что разработчики могут использовать Aspect.NET как часть Visual Studio, с ее многочисленными возможностями для разработки программного обеспечения (сборка, отладка, профилирование и др.). Аспект в системе Aspect.NET определяется как исходный код класса, аннотированный конструкциями метаязыка Aspect.NET. ML. Структура метаязыка Aspect.NET.ML и его семантика не привязаны к конкретному языку реализации, что позволяет использовать одни и те же спецификации аспектов на Aspect.NET.ML в сочетании с различными языками их реализации. Подобная языковая независимость метаязыка АОП - важный принцип Aspect.NET. Вставка кода действий аспекта выполняется автоматически подсистемой внедрения аспектов (weaving). Aspect.NET использует статическое внедрение на уровне промежуточного языка платформы .NET MSIL (Microsoft Intermediate Language), благодаря чему достигается высокая производительность результирующего приложения.
Цель данной работы - исследование вариантов практического применения АОП при разработке программных систем, а также демонстрация использования аспектно-ориентированного подхода как при решении сугубо прикладных задач
в коммерческих проектах, так и для реализации классических методологий программирования. В частности, в статье рассматривается проблема управления сквозной функциональностью в приложениях для Microsoft BizTalk Server, а также применение АОП и системы Aspect.NET для поддержки технологии контрактного проектирования (Design-by-Contract) в .NET-приложениях.
Применение методов АОП в приложениях для Microsoft BizTalk Server. MS BizTalk Server [5] - программный продукт корпорации Microsoft, предназначенный для решения задач комплексной автоматизации бизнес-процессов и интеграции разнородных систем. Приложения для Microsoft BizTalk Server, как правило, представляют собой достаточно сложную конструкцию из взаимосвязанных модулей - артефактов BizTalk, собственных вспомогательных библиотек с кодом, библиотек сторонних разработчиков и т. д. Эти особенности приложений для Microsoft BizTalk Server неизбежно поднимают проблему управления сквозной функциональностью. Данная проблема усугубляется тем, что многие артефакты BizTalk не имеют представления в виде исходного кода в привычном смысле этого слова, вместо этого они представлены в виде описания на некотором метаязыке.
К счастью, практически все артефакты MS BizTalk Server при компиляции превращаются в обыкновенные .NET-сборки, что делает возможным единообразную работу с ними при помощи инструментов для работы с MSIL-кодом. В частности, эта особенность дает возможность применить методы аспектно-ориентированного программирования. Отметим, что не всякий инструмент аспектно-ориентированного программирования подходит для решения указанных задач, а лишь поддерживающий статическое внедрение действий аспектов на уровне двоичного кода. Статическое внедрение на уровне исходного кода невозможно, т. к. артефакты BizTalk имеют разную природу и описываются различными метаязыками
(например, XLANG/s). Динамическое внедрение действий аспекта во время выполнения существенно снижает производительность приложения.
Основная идея применения аспектно-ориентированного подхода и системы Aspect. NET в приложениях для BizTalk заключается в следующем. Как уже отмечалось выше, многие артефакты BizTalk описываются с помощью специальных метаязыков. В случае оркестровок таким языком является XLANG/s, схем сообщений -язык XSD, каналов доставки - типизированный XML, трансформаций - XSL. Всякий из артефактов при компиляции превращается в обыкновенную .NET-сборку. Однако ни для одного из упомянутых языков не существует компилятора в MSIL-код. Вместо этого непосредственно перед компиляцией происходит конвертация конкретного метаязыка в код на языке C#, из которого средствами встроенного компилятора получается двоичный MSIL-код. При детальном анализе генерируемого C# кода можно выявить соответствия между конструкциями исходного метаязыка и объектами C#. Основываясь на выявленных соответствиях, можно создать библиотеку аспектов для решения конкретной задачи в терминах конструкций языка С#, c последующим внедрением в двоичный код. Отметим, что такой подход к применению АОП в проектах для BizTalk
является не только безопасным и надежным методом добавления сквозной функциональности, но и зачастую единственно возможным, т. к. позволяет выйти за рамки предопределенного поведения структурных элементов метаязыков. Рассмотрим несколько сценариев практического применения предложенного подхода и системы Aspect.NET в приложениях для MS BizTalk Server.
Использование Aspect.NET для преодоления ограничений языка XLANG/S. Ярким примером ограничений, налагаемых текущей версией XLANG/s, является отсутствие поддержки пользовательских блоков Finally при обработке ошибок. Конвертер самостоятельно определяет, какие ресурсы необходимо освободить, и генерирует соответствующий код метода Finally. Однако он анализирует лишь «родные» объекты BizTalk, игнорируя созданные пользователем вспомогательные .NET-сущности, которые также могут нуждаться в очистке перед уничтожением (например, объекты, реализующие интерфейс IDisposable).
При помощи Aspect.NET описанное выше ограничение можно с легкостью преодолеть. Для этого необходимо создать аспект, выполняющий всю необходимую работу по очистке ресурсов, и внедрить его непосредственно перед каждым вызовом метода Finally (листинг 1).
■¡before %call Finally(..) %action
public static void CleanupResources()
{
//Perform any needed cleanup actions
Листинг 1: Пример аспекта для пользовательской очистки ресурсов
После внедрения данного аспекта перед каждым вызовом «сгенерированного» метода Finally будет вызываться метод CleanupResources, реализующий всю необходимую функциональность по дополнительной очистке «собственных» ресурсов.
Использование Aspect.NET для задач протоколирования оркестровок. Другой важной проблемой, стоящей перед разработчиками BizTalk-приложений и не имеющей в настоящий момент удобного решения, является протоколирование (logging) оркестровок. Суть проблемы заключается в том, что лишь несколько структурных элементов оркестровок (shapes) допускают вставку дополнитель-
ного пользовательского C# кода, который, в т. ч. может осуществлять протоколирование.
Основная часть конструкций XLANG/s оперирует исключительно в терминах данного языка, не имеющего инструментов для ведения лога. Следствием этого является либо полный отказ разработчика от протоколирования, либо добавление в оркестровку огромного числа дополнительных структурных элементов под названием Expression, допускающих непосредственную вставку C# кода. Во втором случае оркестровка теряет свою наглядность, и образуются дополнительные издержки времени выполнения. Кроме того, в силу упомянутой выше проблемы «черного
Экспериментальная оркестровка
ящика» при конвертации в C#, нет никаких гарантий, что состояние, записанное в лог в элементе Expression, будет адекватно отражать состояние оркестровки в непосредственно следующем за ним другом структурном элементе. Как правило, разработчики не прибегают к протоколированию оркестровок из-за указанных сложностей и довольствуются исследованием системы (с помощью стандартных административных средств MS BizTalk Server) непосредственно перед и после выполнения оркестровки.
Тем не менее, применив аспектно-ориенти-рованный подход, можно без потери наглядности и производительности решить задачу протоколирования оркестровки. Для этого необходимо знать алгоритм работы конвертера, чтобы сопоставить структурные элементы XLANG/s и конструкции языка C#. Однако соответствующее сопоставле-
ние можно также провести на основе «ручного» анализа графического представления оркестровки и генерируемого на ее основе С# кода.
Рассмотрим простую оркестровку, представленную на рисунке. Предположим, что нам необходимо протоколировать информацию обо всех входящих сообщениях. За прием сообщений отвечает порт SampleReceivePort и структурный элемент SampleReceive, составляющие единую логическую сущность (названия назначаются разработчиком самостоятельно).
Отметим еще раз, что в данном случае вставить код для протоколирования стандартными средствами BizTalk в принципе невозможно. После компиляции проекта с оркестровкой откроем соответствующий файл с С# кодом и найдем все вхождения фразы «SampleReceivePort» в сгенерированном коде (листинг 2).
case 3: if
(!SampleReceivePort.GetMessageId(_ctx0_._subWrapperO.getSubscripti
on(this), _seg_, _ctxl_, out _msgEnv_))
return Microsoft.XLANGs.Core.StopConditions.Blocked;
if (_ctxl_._SampleXmlMessage != null)
_ctxl_.UnrefMessage(_ctxl_._SampleXmlMessage);
_ctxl_._SampleXmlMessage = new
_messagetype_System_Xml_XmlDocument("SampleXmlMessage", _ctxl_);
_ctxl_.RefMessage(_ctxl_._SampleXmlMessage);
SampleReceivePort.ReceiveMessage(0, _msgEnv_,
_ctxl_._SampleXmlMessage, null,
(Microsoft.XLANGs.Core.Context)_stateMgrs[l], _seg_);
Листинг 2: Фрагмент сгенерированного C# кода
Очевидно, что непосредственно за прием сообщений в данном фрагменте кода отвечает метод SampleReceivePort.ReceiveMessage, выделенный полужирным курсивом. Очевидно, что всякая при-
емка сообщений осуществляется посредством метода ReceiveMessage. Это дает возможность создать аспект для протоколирования информации обо всех входящих в оркестровку сообщений (листинг 3).
%after %call ReceiveMessage(..) %action
public static void LogRecieveInfo(int ctxId,
Microsoft.XLANGs.Core.Envelope envelope,
messagetype System Xml XmlDocument message)
{
}
//Log message information
Листинг 3: Пример аспекта для протоколирования входящих сообщений
Методы аспектно-ориентированного программирования могут с успехом применяться и к остальным артефактам BizTalk - схемам (schémas), трансформациям (maps), каналам доставки (pipelines). Применение Aspect.NET к данным артефактам возможно благодаря тому, что, несмотря на различную природу, все указанные артефакты компилируются в обыкновенные .NET-сборки.
Применение АОП и системы Aspect.NET для поддержки технологии Design-by-Contract. Технология проектирования по контракту (Design-by-Contract) [6] - процесс проектирования, предполагающий разработку формальных, точных и верифицируемых спецификаций для каждого программного элемента. Контрактная спецификация представляет собой набор утверждений (предусловия, постусловия и инварианты), четко описывающих, что должен и не должен делать каждый конкретный метод. Технология была предложена Бертраном Мейером еще в 1986 г. в контексте разработки языка программирования Eiffel, однако сегодня его идея особенно актуальна в рамках задачи эффективного повторного использования так называемых «надежных компонентов» (trusted components), в корректности которых не должно быть никаких сомнений.
Если некоторый компонент предоставляет окружению свою функциональность, он может наложить предусловие (precondition) на ее использование. Предусловия выражают ограничения, выполнение которых необходимо для корректной работы программы. Корректная система никогда не вызовет функциональность, если не выполняется ее предусловие. В свою очередь компонент может гарантировать выполнение некоторого
действия с помощью постусловия (postcondition). Постусловие определяет состояние, завершающее выполнение программы. Инвариант (invariant) класса - это утверждение, выражающее фундаментальные соотношения, характерные для данного класса. Инвариант применяется к классу как целому, и этим отличается от предусловий и постусловий, характеризующих отдельные методы. Инвариант должен выполняться перед вызовом и после вызова каждого метода данного класса.
Язык Бертрана Мейера Eiffel не является об-щеупотребимым, что препятствует распространению концепции контрактного проектирования на практике. В то же время в традиционных языках программирования при реализации контрактных пред- и постусловий разработчик сталкивается со следующими сложностями.
• Во-первых, код проверки пред- и постусловий перемешивается с основным кодом компонента. Это снижает ясность кода и, что более важно, снижает способность программного компонента к повторному использованию. Например, если в другой системе будут более жесткие требования к производительности, может возникнуть необходимость отключить проверки, что неизбежно ведет к модификации кода.
• Во-вторых, код проверки пред- и постусловий рассредоточивается по всей системе. Если возникнет потребность изменить какое-либо из условий, то придется произвести изменения во всех модулях, на которые оно распространяется. При этом достаточно тяжело поддерживать систему в согласованном состоянии.
Проверка утверждений контракта - типичный пример сквозной функциональности, реализация которой присутствует во многих про-
4
граммных модулях. В рамках классического объектно-ориентированного подхода не существует возможности локализовать в отдельные модули функциональность, которая пронизывает всю систему. В данной работе предлагается реализовать контрактное проектирование с помощью аспектно-ориентированного программирования [7]. При та-
ком подходе основная бизнес-логика компонента не изменяется, контрактная спецификация реализуется в отдельном программном модуле - аспекте. После чего происходит автоматическое внедрение контрактных аспектов в заданные точки программы на уровне бинарных файлов и сборок.
Рассмотрим небольшой пример (листинг 4).
public static void LoadInnerEntities(IEnumerable<Tender> tenders) {
// possible ArgumentNullException, ArgumentException foreach (Material material in materials)
dicMaterials.Add(material.Id, material); foreach (Supplier supplier in suppliers)
dicSuppliers.Add(supplier.Id, supplier); foreach (Price price in prices)
dicPrices.Add(price.Id, price);
// possible ArgumentNullException, KeyNotFoundException
foreach (Tender tender in tenders) {
tender.Material = dicMaterials[tender.MaterialId]; tender.Supplier = dicSuppliers[tender.SupplierId]; tender.Price = dicPrices[tender.PriceId];
}
Листинг 4: Пример целевого приложения
Представленный фрагмент исходного кода целевого приложения содержит несколько потенциально уязвимых мест при работе со словарем. Следуя принципам контрактного проектирования, прежде чем вызвать метод, разработчик должен убедиться, что выполнены его предусловия. То есть перед добавлением элемента нужно проверить, что элемента с таким ключом еще нет в словаре. А перед извлечением - что такой ключ в словаре, наоборот, присутствует. В обоих случаях нужно также убедиться, что передаваемый ключ не равен нулевой ссылке. Если добавлять эти проверки напрямую в код, объем данного фрагмента возрастет как минимум в полтора раза. Не говоря уже о том, что на сопровождение и внесение изменений в готовый программный код, по оценкам исследователей, тратится около 70 % времени работы разработчика.
Вместо этого реализуем контрактную спецификацию в виде аспекта (листинг 5).
При внедрении данного аспекта в указанные точки исходного кода, получаем точно такую же программу, как если бы все проверки вносились
вручную. При этом имеем следующие преимущества.
• Во-первых, обеспечивается ясность, наглядность и легкость сопровождения исходного кода программы. Основной код компонента не подвергается изменениям, интеграция контракта осуществляется аспектным компоновщиком на уровне бинарных файлов и сборок.
• Во-вторых, АОП предоставляет возможность автоматического добавления новой сквозной функциональности в код целевых приложений, в отличие от традиционных сред разработки, в которых эти операции приходится выполнять вручную.
• В-третьих, используя графический пользовательский интерфейс системы Aspect.NET, можно настраивать необходимый уровень проверок: включить все проверки, оставить только предусловия или же полностью отключить (например, в конечной версии продукта, если имеются жесткие требования к производительности). При этом, поскольку исходный код компонента не модифицируется, полностью сохраняется его способность к повторному использованию.
[AspectDescription(«Contract for IDictionary»)]
public class DictionaryAspect : Aspect {
[AspectAction(«%before %call IDictionary.Add»)]
public static void CheckAddPrecondition() {
IDictionary targetObject = (IDictionary)TargetObject; Contract.Requires<ArgumentNullException>(key != null); Contract.Requires<ArgumentException>(!targetObject.Contains(key));
}
[AspectAction(«%before %call IDictionary.get Item»)]
public static void CheckGetItemPrecondition() {
IDictionary targetObject = (IDictionary)TargetObject; Contract.Requires<ArgumentNullException>(key != null); Contract.Requires<KeyNotFoundException>(targetObject.Contains(key));
}
Листинг 5: Реализация контрактной спецификации в виде аспекта
• В-четвертых, АОП широко поддерживается различными средами разработки ПО, что способствует распространению концепции контрактного проектирования на практике.
Microsoft BizTalk Server, являясь мощным и гибким инструментом автоматизации бизнес-процессов, не лишен ряда существенных недостатков. Характерными проблемами являются управление сквозной функциональностью и преодоление ограничений метаязыков, используемых для описания артефактов BizTalk. Данные проблемы могут эффективно решаться с помощью аспектно-ориентированного программирования. Благодаря возможности работать с дво-
ичными .КЕТ-сборками, инструмент Aspect.NET позволяет решать такие задачи, как преодоление ограничений метаязыка XLANG/s, протоколирование оркестровок, автоматизация обобщенных бизнес-процессов и другие задачи, связанные с добавлением или изменением функциональности в готовых BizTalk-артефактах и приложениях.
В статье продемонстрировано применение аспектно-ориентированного программирования для реализации контрактного проектирования. Аспектно-ориентированный подход, безусловно, решает проблемы, связанные с запутанным и рассредоточенным кодом, и предоставляет простое и мощное решение для реализации проверки проектных контрактов.
список литературы
1. Kiczales, G. Aspect-oriented programming [Текст] / G. Kiczales, J. Lamping, A. Mendhekar [et al.] // Proc. of the European Conference on Object-Oriented Programming (ECOOP). -June 1997. -P. 220-242.
2. Safonov, V.O. Using aspect-oriented programming for trustworthy software development [Текст] / V.O. Safonov. -John Wiley & Sons, 2008. -338 p.
3. Майерс, Г. Надежность программного обеспечения [Текст] / Г. Майерс; пер. с англ. Ю.Ю. Галимова; под ред. В.Ш. Кауфмана. -М.: Мир, 1980. -360 c.
4. Сафонов, В.О. Aspect.NET - инструмент аспектно-ориентированного программирования для разработки надежных и безопасных программ [Текст] /
В.О. Сафонов // Компьютерные инструменты в образовании. -2007. -№ 5. -С. 3-13.
5. Beckner, M. BizTalk 2006 recipes: a problem-solution approach [Текст] / M. Beckner, B. Goeltz, B. Gross [et al.]. -Apress, 2006. -534 p.
6. Meyer, B. Object-oriented software construction, [Текст] / B. Meyer; 2nd ed. -Prentice Hall, 1997. -1254 p.
7. Когай, А.Р. Применение аспектно-ориенти-рованного программирования для поддержки технологии Design-by-Contract [Текст] / А.Р. Когай // Компьютерные инструменты в образовании. - 2010. - № 4. -С. 12-20.