В мире разработки программного обеспечения постоянно появляются новые парадигмы и подходы, призванные упростить создание, поддержку и масштабирование сложных систем. Одним из таких подходов является Аспектно-ориентированное программирование (АОП). В сообществе 1С нередко поднимается вопрос о связи между классическими парадигмами программирования и уникальными инструментами платформы 1С:Предприятие. В частности, мы с вами рассмотрим, как механизм расширений 1С, который стал неотъемлемой частью современной разработки, перекликается с принципами АОП. Мы углубимся в основы АОП, проанализируем его ключевые концепции и выясним, насколько эти идеи нашли свое отражение в технологии расширений 1С, а также какие возможности для доработки и совершенствования платформы в этом направлении еще существуют.
Для начала давайте подробно разберем, что же такое Аспектно-ориентированное программирование. АОП представляет собой парадигму программирования, основная цель которой — повысить модульность программного обеспечения. Это достигается за счет эффективного и четкого разделения так называемых «сквозных задач» (cross-cutting concerns). Что же это за «сквозные задачи»?
Представьте себе типичное корпоративное приложение. В нем почти всегда присутствуют такие виды функциональности, как логирование (запись информации о событиях), кэширование данных (для ускорения работы), аутентификация и авторизация пользователей (проверка их личности и прав), обработка ошибок (единообразное реагирование на сбои) или управление транзакциями (обеспечение атомарности операций с базой данных). Проблема заключается в том, что логика для этих задач обычно «размазывается» по множеству модулей приложения. Например, код для логирования может встречаться почти в каждом методе бизнес-логики, код проверки прав — перед вызовом важных функций, а код обработки транзакций — вокруг операций с данными.
Такое распределение сквозной функциональности приводит к нескольким серьезным проблемам:
Именно для решения этих проблем и было предложено АОП. АОП позволяет нам выделить эту сквозную функциональность в отдельные, специализированные модули, которые мы называем аспектами. Эти аспекты могут быть «вплетены» в основной код программы, дополняя его функциональность без необходимости прямого изменения исходного кода. В результате мы получаем более чистую архитектуру, улучшенную модульность, упрощенное сопровождение и повышенную гибкость.
Для более глубокого понимания того, как АОП может быть связано с расширениями 1С, нам необходимо подробно рассмотреть основные концепции этой парадигмы. Давайте разберем каждую из них по шагам:
Аспект — это центральное понятие в АОП. Мы можем представить аспект как независимый модуль или класс, который инкапсулирует в себе логику для конкретной сквозной задачи. Например, у нас может быть аспект "Логирование", который будет отвечать за запись всех важных событий в системе, или аспект "Безопасность", который будет выполнять проверки прав доступа перед выполнением определенных операций. Аспект определяет, что нужно сделать (т.е. какую функциональность предоставить) и где это нужно сделать (т.е. в каких точках программы).
Точки соединения — это конкретные, четко определенные моменты или места в процессе выполнения программы, где может быть применен аспект. Этими точками могут быть различные события в жизненном цикле приложения, такие как:
Каждая такая точка является потенциальным местом для "внедрения" аспектной логики. Мы можем думать о них как о "крючках" или "точках расширения", которые существуют в нашем коде.
Совет — это фактическая реализация задачи, которую выполняет аспект в определенной точке соединения. По сути, это код, который "вплетается" в наш основной код. Существует несколько типов советов, каждый из которых определяет, когда именно будет выполнен код аспекта относительно точки соединения:
ЗаписатьДокумент() мы можем выполнить проверку прав пользователя или валидацию данных.finally в конструкциях try-catch-finally многих языков программирования, идеален для очистки ресурсов.Срезы — это высокоуровневые выражения или шаблоны, которые используются для точного описания набора точек соединения, где должен быть применен один или несколько советов. Мы можем думать о срезе как о запросе, который выбирает нужные нам "крючки" в коде. Например, срез может быть определен как "все вызовы методов, начинающихся с 'Записать' в модулях объектов документов", или "все операции чтения из базы данных".
Целью является объект или набор объектов (классов), к которым применяются аспекты и их советы. Это тот кусок кода, в который мы "вплетаем" нашу аспектную логику.
Плетение — это процесс связывания аспектов с другими объектами для создания конечного, модифицированного исполняемого кода. Этот процесс может происходить на разных этапах жизненного цикла программы:
Механизм плетения определяет, когда и как аспекты оказывают влияние на целевой код.
Теперь, когда мы освежили в памяти основные концепции АОП, давайте проанализируем, как они соотносятся с технологией расширений конфигурации в платформе 1С:Предприятие. Мы увидим, что расширения демонстрируют весьма схожие принципы, что делает их мощным инструментом, близким по духу к АОП.
Фундаментальное сходство: изменение без модификации исходного кода
Ключевое свойство расширений 1С, которое сразу же роднит их с АОП, — это возможность изменять и дополнять функциональность существующей конфигурации без непосредственного изменения ее исходного кода. Почему это так важно для 1С-разработчиков? Типовые конфигурации 1С постоянно обновляются вендором. Любое прямое изменение типового кода приводит к усложнению процесса обновления: каждое обновление требует слияния (merge) всех доработок с новой версией, что может быть трудоемким и чреватым ошибками. Расширения же позволяют нам внедрять дополнительное поведение, сохраняя при этом типовую конфигурацию на стандартной поддержке, что значительно упрощает ее сопровождение и обновление.
Внедрение дополнительного поведения (Аспекты)
Подобно тому, как аспекты внедряют свою логику в основной код, расширения позволяют нам добавлять или модифицировать существующую логику в определенных "точках" конфигурации. Мы можем:
Это по сути и есть реализация принципа "аспектов" — мы инкапсулируем нашу дополнительную функциональность в рамках расширения и применяем ее к типовой конфигурации.
Точки соединения (Joinpoints) в 1С
В контексте 1С:Предприятия "точками соединения", где мы можем "вплетать" нашу аспектную логику с помощью расширений, являются:
ПриСозданииНаСервере, ПередЗаписьюНаСервере, ПослеЗаписиНаСервере, ПриЗакрытии для форм документов, справочников, отчетов. Мы можем добавить свою логику до или после выполнения стандартных действий.ПередЗаписью, ПриЗаписи, ОбработкаПроведения для документов, ПередУдалением для объектов. Эти точки позволяют нам влиять на бизнес-логику на уровне данных._ПриИзменении, _ПередЗаписьюРасширения). Эти методы являются явными "точками расширения", предложенными разработчиками конфигурации.Все эти механизмы позволяют нам внедрять дополнительную логику в работу конфигурации, не изменяя ее основу. Это способствует централизации сквозных задач, таких как дополнительная валидация данных, ведение аудита изменений, интеграционные хуки или специфические изменения пользовательского интерфейса, не "размазывая" их по всему коду типовой конфигурации.
Давайте подробнее рассмотрим, как различные типы советов АОП могут быть реализованы или имитированы с помощью механизмов расширений и подписок 1С:Предприятия.
Этот тип поведения очень хорошо реализуется в 1С. Мы можем добавить свою логику "до" выполнения стандартной операции. Типичные примеры:
ПередЗаписьюНаСервере или ПриИзменении для элементов формы. Здесь мы можем проверить корректность введенных данных или изменить их перед тем, как стандартная логика начнет свою работу.ПередЗаписью или ПередУдалением для объектов. Наша процедура-обработчик будет вызвана до того, как система попытается записать или удалить объект. Если мы установим параметр Отказ = Истина, то стандартное действие будет отменено.Пример (концептуальный):
// В расширении:
// Создаем подписку на событие "ПередЗаписью" для документа "РеализацияТоваровУслуг"
// В модуле ПодпискиНаСобытияРасширения:
Процедура ПроверкаСуммыРеализацииПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
Если ТипЗнч(Источник) = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
Если Источник.СуммаДокумента < 0 Тогда
Отказ = Истина;
Сообщить("Сумма документа не может быть отрицательной!", СтатусСообщения.Важное);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Здесь ПроверкаСуммыРеализацииПередЗаписью действует как "Before" совет.
Логика "After" также легко реализуется в 1С. Мы можем выполнять действия после завершения основной операции, независимо от ее успеха. Примеры:
ПослеЗаписьюНаСервере. Здесь мы можем обновить какие-либо зависимые данные или уведомить пользователя о результате.ПриЗаписи или ПослеУдаления для объектов. Наша процедура-обработчик будет вызвана после того, как объект был записан или удален.Пример (концептуальный):
// В расширении:
// Создаем подписку на событие "ПриЗаписи" для документа "РеализацияТоваровУслуг"
// В модуле ПодпискиНаСобытияРасширения:
Процедура ЛогированиеЗаписиРеализации(Источник, Отказ) Экспорт
Если ТипЗнч(Источник) = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
ЖурналРегистрации.Записать(
"Запись документа реализации",
УровеньЖурналаРегистрации.Информация,
Метаданные.Документы.РеализацияТоваровУслуг,
Источник,
"Документ реализации " + Источник.Номер + " от " + Источник.Дата + " успешно записан."
);
КонецЕсли;
КонецПроцедуры
Здесь ЛогированиеЗаписиРеализации действует как "After" совет.
Прямого и отдельного типа перехватчика "After returning" в 1С нет. Однако мы можем имитировать это поведение, если точка соединения является функцией, результат которой мы можем проанализировать, или если мы используем "After" совет и в его рамках проверяем успешность выполнения предыдущей операции. Например, после записи документа можно проверить наличие ошибок в результате операции.
В 1С для обработки исключений мы традиционно используем конструкцию Попытка ... Исключение ... КонецПопытки. Мы можем обернуть вызовы стандартных методов в такие блоки, чтобы перехватывать и обрабатывать ошибки. Однако это требует изменения *внедряемого* кода (например, в модуле расширения), а не является системным аспектом, который автоматически перехватывает *любое* исключение в заданной точке. Для системного перехвата ошибок в 1С используются другие механизмы, например, обработчики ошибок для COM-соединений или специализированные подсистемы вроде "Управление доступом" для контроля прав.
Аналогично "After throwing", этот совет имитируется с помощью блока Попытка ... Исключение ... КонецПопытки. Код, который должен быть выполнен всегда, располагается в конце блока Попытка (если нет явного блока Исключение после которого нужно продолжить выполнение) или после всего блока, если нет Возврат внутри. На уровне платформы нет встроенного механизма "finally" для произвольных точек соединения.
Ключевое ограничение: отсутствие прямого аналога "Around" совета
Однако, как справедливо было отмечено в исходном обсуждении, 1С еще есть что дорабатывать в этой области, особенно если сравнивать с более развитыми АОП-фреймворками. Критическим отличием является отсутствие прямого, декларативного аналога "Around" совета, который давал бы полный контроль над вызовом метода.
Напомним, что совет типа "Around" предоставляет разработчику максимальный контроль над выполнением метода. Он позволяет не только выполнить логику до и после, но и:
В 1С, когда мы переопределяем процедуру или функцию в расширении (например, используя механизм "добавить" или "изменить" в общих модулях, или посредством подписок на события), мы обычно добавляем логику до или после стандартного выполнения. Но у нас нет возможности просто и декларативно заблокировать или полностью подменить основной вызов без написания сложной обертки или (что хуже) модификации самого метода-источника. Механизм подписок на события позволяет выполнять код до и после, и даже отменить стандартное действие (через параметр Отказ = Истина), но это не эквивалентно полному контролю, который предлагает "Around".
Для полной замены метода в 1С без изменения конфигурации приходится прибегать к следующим подходам:
Отсутствие мощного "Around" совета означает, что разработчикам 1С приходится искать обходные пути, если требуется полностью контролировать или заменять выполнение стандартных методов. Это может привести к менее элегантным решениям или, в худшем случае, к необходимости частичного изменения типового кода, что является анти-паттерном для расширений.
Важно также рассмотреть, как происходит "плетение" аспектов в 1С. В платформе 1С:Предприятие плетение расширений происходит во время выполнения (Runtime weaving). Это означает, что:
Такой подход обеспечивает высокую гибкость и позволяет применять изменения "на лету", без перекомпиляции всей системы, что идеально подходит для среды 1С с ее динамическими обновлениями и адаптацией под нужды клиента.
Несмотря на некоторые ограничения, подход, реализуемый расширениями 1С, приносит значительные преимущества, которые мы можем ассоциировать с принципами АОП:
Осознанное применение принципов АОП может значительно улучшить качество наших решений, создаваемых с помощью расширений 1С. Вот несколько практических советов:
Выявление таких задач позволяет нам проектировать модульные и переиспользуемые расширения.
ПередЗаписью для всех нужных объектов. Для логирования после успешной операции — ПриЗаписи.
Пример: Аудит изменения статуса документа.
// В расширении:
// Создаем ПодпискуНаСобытие с именем "АудитИзмененияСтатуса"
// Источник: ДокументСсылка.ЗаказКлиента (или нужный тип объекта)
// Событие: ПриЗаписи
// Обработчик: ПодпискиНаСобытияРасширения.АудитИзмененияСтатусаПриЗаписи
// В модуле ПодпискиНаСобытияРасширения:
Процедура АудитИзмененияСтатусаПриЗаписи(Источник, Отказ) Экспорт
Если ТипЗнч(Источник) = Тип("ДокументОбъект.ЗаказКлиента") Тогда
// Предполагаем, что Статус - реквизит, и можем получить старое значение
// (в реальности для аудита старых значений требуется отдельный механизм,
// например, чтение из истории объекта или временное хранение)
СтарыйСтатус = ДополнительныеСвойстваОбъекта.ПолучитьСтатусДоЗаписи(Источник.Ссылка); // Концептуально
Если Источник.Модифицирован И Источник.Статус <> СтарыйСтатус Тогда
// Если статус изменился, записываем в наш журнал аудита
МойЖурналАудита.ЗаписатьСобытие( // Концептуальный вызов
"ИзменениеСтатусаДокумента",
"Документ ЗаказКлиента " + Источник.Номер + " от " + Источник.Дата
+ " изменил статус с " + СтарыйСтатус
+ " на " + Источник.Статус
);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В этом примере АудитИзмененияСтатусаПриЗаписи действует как "After" совет, срабатывающий после записи документа, для сквозной задачи аудита.
ПриИзменении, ПриНачалеРедактирования). Это по сути является внедрением логики "Before" или "After" для интерактивных действий пользователя.
Пример: Добавление специальной кнопки на форму списка документов.
// В расширении:
// Открываем форму списка документов "РеализацияТоваровУслуг".
// Добавляем новую команду "СформироватьОтчетПоВыделенным"
// Добавляем кнопку для этой команды на командную панель формы.
// В модуле формы расширения (для модифицированной формы):
Процедура СформироватьОтчетПоВыделенным(Команда)
Если Элементы.Список.ТекущаяСтрока = Неопределено Тогда
Сообщить("Выберите документы для формирования отчета.");
Возврат;
КонецЕсли;
// Получаем выделенные строки
ВыделенныеСтроки = Новый Массив;
Для Каждого СтрокаТЗ Из Элементы.Список.ВыделенныеСтроки Цикл
ВыделенныеСтроки.Добавить(СтрокаТЗ.Ссылка);
КонецЦикла;
Если ВыделенныеСтроки.Количество() > 0 Тогда
// Здесь вызываем наш отчетный модуль, передавая ему массив ссылок
МойМодульОтчетовРасширения.СформироватьОтчет(ВыделенныеСтроки); // Концептуальный вызов
Иначе
Сообщить("Документы не выбраны.");
КонецЕсли;
КонецПроцедуры
Эта процедура действует как "After" совет для действия пользователя (нажатия кнопки).
В ходе нашего анализа мы выяснили, что технология расширений 1С, хоть и не является полноценной и всеобъемлющей реализацией Аспектно-ориентированного программирования в классическом смысле, тем не менее, воплощает многие из его ключевых принципов и предоставляет разработчикам мощные инструменты для работы со сквозными задачами. Возможность внедрения дополнительного поведения без изменения исходного кода конфигурации, четкое разделение сквозных задач, улучшенная модульность и простота сопровождения — все эти аспекты делают расширения 1С инструментом, глубоко укорененным в идеях АОП.
Понимание концепций АОП, таких как аспекты, точки соединения, советы и плетение, помогает нам осознанно и эффективно подходить к проектированию и разработке доработок в 1С. Оно позволяет нам создавать более чистые, модульные, легко поддерживаемые и масштабируемые решения, которые гармонично сосуществуют с типовой функциональностью. Да, платформа 1С еще имеет потенциал для развития, особенно в части предоставления более гранулированного контроля над вызовами методов, сопоставимого с "Around" советом в классическом АОП. Однако уже сейчас мы располагаем обширным арсеналом механизмов для эффективного использования принципов АОП. Продолжаем развиваться, изучать новые подходы и применять эти знания на практике, чтобы наши решения в 1С становились еще более совершенными и отвечали самым высоким стандартам разработки.