среда, 7 октября 2009 г.

Манифест некоммунистической партии.

Категорически приветствую вас, уважаемые коллеги!

Наконец-то(!) появилось время что-нибудь черкнуть не только на языке компьютера и не для компьютера =).
Два больших и интересных проекта поглотили меня с головой. Возможно, как-нибудь расскажу и о них. Тем более, там есть о чем поведать... Но сегодня я хотел поговорить о другом. На днях столкнулся с проблемой, которая помимо ассоциации со знаменитым трудом Карла Генриха Маркса и Фридриха непоймикого Энгельса привела меня в замешательство. Есть у меня один проект, работа которого под правильными операционными системами не возможна без запроса определенных прав.
Для получения этих прав в манифесте, находящемся в ресурсах приложения, необходимо указать начальный уровень привилегий. Подробно об этом можно прочитать на MSDN или в новом блоге Александра Божко.
Надо отметить, что ранее (в D2007) особых проблем с манифестами не было, так как они добавлялись руками трудолюбивых разработчиков. Теперь же, в эпоху тотально автоматизации создатели передовой IDE решили помочь разработчикам, избавив их от рутинной работы. Тепрь D2010 сама прикрутит за вас манифест со своими параметрами, изменить которые в проекте нет никакой возможности. Прямо как в том мультфильме:
— Так вы что, и конфеты за меня есть будете?!.. — АГА!!!

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

Что ж, мы, как говориться, не ищем легких путей, да и покой нам только снится. К сожалению, FinalBuilder выдирать манифесты ещё не научился. А готовой консольной программы, которая могла бы запускаться из пакетных файлов, вырезать ресурсы и при этом корректно отдавать ExitCode, я за приемлемое время не нашел. Пришлось достать коленку, и на ней написать патчилку косяка. Вот, может быть кому-нибудь ещё пригодиться.

MD5 архива: 3abd737e5d990240b08d856f487a4b3b
MD5 ехе: 3b18c4323ec1226bbf466515c86ce1fa

понедельник, 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.

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

понедельник, 29 декабря 2008 г.

АОП и Delphi (Часть 1)

Давно хотел поговорить про аспектно-ориентированное программирование. В сети про это дело написано немало, я же хотел взглянуть на АОП применительно к Delphi. Но чтобы начать предметное обсуждение, необходимо вначале хотя бы упомянуть о сквозной функциональности, которая, по сути, и явилась катализатором появления АОП. Классическим примером сквозной функциональности является трассировка программы или логгирование действий. Чтобы было все предельно ясно, приведу пример.
Допустим, у вас есть программа, которая соединяется с некоторым сервером, передает ему данные и заканчивает сеанс связи. Протокол для связи вы написали сами и оформили в отдельный модуль или даже компонент. Он надежен, закончен и самодостаточен. И тут возникла необходимость залоггировать действия программы на этапе соединения. По сути дела, следует залезть в отлаженный и красивый код, напихать внутрь много разных странных строк, которые сделают его перегруженным и сложно читаемым, запутают основную логику модуля или, говоря другими словами, безнадежно его испортят. Так подумали и создатели системы тестирования компонентов Кактус, сели, задумались и родили аспектно-ориентированное программирование и его первую реализацию - AspectJ. Но пока мы до него не добрались, давайте посмотрим, что из того, что уже есть в Delphi, поможет нам хоть как-то исправить положение.
Частично решить проблему сквозной функциональности могут хорошо продуманные события компонента, предоставляемые разработчиком компонента пользователю. Конечно, идеальный вариант – это когда разработчик и пользователь – одно лицо, но и просто наличие исходников уже большой плюс. Но как я уже говорил, решение это - частичное. Во-первых, и глупо, и бессмысленно на каждый чих создавать события. Во-вторых, оригинальный код всё равно придется править, а значит вносить в него дополнительные ошибки и тратить время на отладку. В-третьих, проблема раздувания исходного кода компонента ненужной всякий раз логикой в полной мере не решается. Вывод: способ применим, местами удобен, местами нет, проблему полностью не снимает.
Вторым, более современным подходом является использование хелпера. Действительно, в этом случае возникает ситуация, обратная той, что рассматривалась в предыдущем примере – мы не правим исходный код, а лишь дорабатываем те места, где требуется вмешательство и изменение логики. Однако и тут полноценного решения проблемы нет, так как мы не избавлены от внесения сквозной функциональности в тела методов, хотя теперь вряд ли загубим работающий класс необдуманными действиями.
Еще одним интересным решением является использование RTTI информации для перехвата вызова того или иного метода класса. Об этом мы поговорим чуть позже, когда будем подробнее рассматривать одну из реализаций АОП на Delphi.
Можно, пожалуй, придумать ещё несколько способов, основанных, например, на использовании структурных паттернов, например Proxy или Adapter, но и сами паттерны – это зачастую пример жесточайшего кросскаттинга, так что всё это будет далеко от тех идей, которые несет АОП.
(продолжение следует)