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

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

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