Как Аспектно-ориентированное программирование (АОП) связано с технологией расширений в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В мире разработки программного обеспечения постоянно появляются новые парадигмы и подходы, призванные упростить создание, поддержку и масштабирование сложных систем. Одним из таких подходов является Аспектно-ориентированное программирование (АОП). В сообществе 1С нередко поднимается вопрос о связи между классическими парадигмами программирования и уникальными инструментами платформы 1С:Предприятие. В частности, мы с вами рассмотрим, как механизм расширений 1С, который стал неотъемлемой частью современной разработки, перекликается с принципами АОП. Мы углубимся в основы АОП, проанализируем его ключевые концепции и выясним, насколько эти идеи нашли свое отражение в технологии расширений 1С, а также какие возможности для доработки и совершенствования платформы в этом направлении еще существуют.

Что такое Аспектно-ориентированное программирование (АОП) и зачем оно нужно?

Для начала давайте подробно разберем, что же такое Аспектно-ориентированное программирование. АОП представляет собой парадигму программирования, основная цель которой — повысить модульность программного обеспечения. Это достигается за счет эффективного и четкого разделения так называемых «сквозных задач» (cross-cutting concerns). Что же это за «сквозные задачи»?

Представьте себе типичное корпоративное приложение. В нем почти всегда присутствуют такие виды функциональности, как логирование (запись информации о событиях), кэширование данных (для ускорения работы), аутентификация и авторизация пользователей (проверка их личности и прав), обработка ошибок (единообразное реагирование на сбои) или управление транзакциями (обеспечение атомарности операций с базой данных). Проблема заключается в том, что логика для этих задач обычно «размазывается» по множеству модулей приложения. Например, код для логирования может встречаться почти в каждом методе бизнес-логики, код проверки прав — перед вызовом важных функций, а код обработки транзакций — вокруг операций с данными.

Такое распределение сквозной функциональности приводит к нескольким серьезным проблемам:

  1. «Размазывание кода» (Code Scattering): Одна и та же логика (например, логирование) дублируется или почти дублируется в разных местах системы, что ведет к избыточности.
  2. «Спутывание кода» (Code Tangling): Код, относящийся к основной бизнес-логике, перемешивается с кодом сквозных задач. Это усложняет чтение, понимание и изменение основной логики.
  3. Сложность сопровождения: Изменение в логике сквозной задачи (например, изменение формата записи логов) требует модификации множества файлов и мест в коде.
  4. Низкая модульность: Модули становятся менее независимыми, так как содержат разнородную функциональность.

Именно для решения этих проблем и было предложено АОП. АОП позволяет нам выделить эту сквозную функциональность в отдельные, специализированные модули, которые мы называем аспектами. Эти аспекты могут быть «вплетены» в основной код программы, дополняя его функциональность без необходимости прямого изменения исходного кода. В результате мы получаем более чистую архитектуру, улучшенную модульность, упрощенное сопровождение и повышенную гибкость.

Ключевые концепции Аспектно-ориентированного программирования

Для более глубокого понимания того, как АОП может быть связано с расширениями 1С, нам необходимо подробно рассмотреть основные концепции этой парадигмы. Давайте разберем каждую из них по шагам:

  1. Аспекты (Aspects):

    Аспект — это центральное понятие в АОП. Мы можем представить аспект как независимый модуль или класс, который инкапсулирует в себе логику для конкретной сквозной задачи. Например, у нас может быть аспект "Логирование", который будет отвечать за запись всех важных событий в системе, или аспект "Безопасность", который будет выполнять проверки прав доступа перед выполнением определенных операций. Аспект определяет, что нужно сделать (т.е. какую функциональность предоставить) и где это нужно сделать (т.е. в каких точках программы).

  2. Точки соединения (Joinpoints):

    Точки соединения — это конкретные, четко определенные моменты или места в процессе выполнения программы, где может быть применен аспект. Этими точками могут быть различные события в жизненном цикле приложения, такие как:

    • Вызов метода или функции.
    • Выполнение метода или функции.
    • Создание нового объекта.
    • Обращение к полю объекта.
    • Обработка исключения.

    Каждая такая точка является потенциальным местом для "внедрения" аспектной логики. Мы можем думать о них как о "крючках" или "точках расширения", которые существуют в нашем коде.

  3. Советы (Advice):

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

    • Before (Перед): Этот совет выполняется перед выполнением кода в точке соединения. Например, перед вызовом метода ЗаписатьДокумент() мы можем выполнить проверку прав пользователя или валидацию данных.
    • After (После): Этот совет выполняется после того, как код в точке соединения завершил свое выполнение, независимо от того, успешно он завершился или нет. Это может быть полезно для освобождения ресурсов или окончательного логирования.
    • After returning (После успешного возврата): Выполняется только после успешного завершения работы кода в точке соединения, то есть если метод вернул значение без возникновения исключений. Мы можем использовать его для пост-обработки результата или для логирования успешных операций.
    • After throwing (После возникновения исключения): Этот совет срабатывает только в случае возникновения исключения во время выполнения кода в точке соединения. Он позволяет централизованно обрабатывать ошибки, регистрировать их или выполнять корректирующие действия.
    • After finally (После завершения, всегда): Выполняется всегда после завершения работы кода в точке соединения, вне зависимости от того, произошло исключение или нет, и было ли возвращено значение. Это аналог блока finally в конструкциях try-catch-finally многих языков программирования, идеален для очистки ресурсов.
    • Around (Вокруг): Этот совет предоставляет наиболее полный и мощный контроль. Он полностью оборачивает вызов метода в точке соединения. Совет "Around" позволяет не только выполнить пред- и пост-обработку, но и полностью предотвратить вызов оригинального метода, изменить его параметры, заменить его собственной реализацией или даже изменить возвращаемое значение. Это дает возможность радикально изменить или даже полностью заглушить стандартное поведение.
  4. Срезы (Pointcuts):

    Срезы — это высокоуровневые выражения или шаблоны, которые используются для точного описания набора точек соединения, где должен быть применен один или несколько советов. Мы можем думать о срезе как о запросе, который выбирает нужные нам "крючки" в коде. Например, срез может быть определен как "все вызовы методов, начинающихся с 'Записать' в модулях объектов документов", или "все операции чтения из базы данных".

  5. Цель (Target):

    Целью является объект или набор объектов (классов), к которым применяются аспекты и их советы. Это тот кусок кода, в который мы "вплетаем" нашу аспектную логику.

  6. Плетение (Weaving):

    Плетение — это процесс связывания аспектов с другими объектами для создания конечного, модифицированного исполняемого кода. Этот процесс может происходить на разных этапах жизненного цикла программы:

    • Во время компиляции (Compile-time weaving): Аспекты "вплетаются" в код непосредственно в процессе компиляции исходных файлов.
    • Во время загрузки классов (Load-time weaving): Аспекты применяются, когда классы загружаются виртуальной машиной.
    • Во время выполнения (Runtime weaving): Аспекты внедряются в уже запущенную программу, динамически изменяя ее поведение.

    Механизм плетения определяет, когда и как аспекты оказывают влияние на целевой код.

Расширения 1С:Предприятия как аналог и шаг к АОП

Теперь, когда мы освежили в памяти основные концепции АОП, давайте проанализируем, как они соотносятся с технологией расширений конфигурации в платформе 1С:Предприятие. Мы увидим, что расширения демонстрируют весьма схожие принципы, что делает их мощным инструментом, близким по духу к АОП.

Фундаментальное сходство: изменение без модификации исходного кода

Ключевое свойство расширений 1С, которое сразу же роднит их с АОП, — это возможность изменять и дополнять функциональность существующей конфигурации без непосредственного изменения ее исходного кода. Почему это так важно для 1С-разработчиков? Типовые конфигурации 1С постоянно обновляются вендором. Любое прямое изменение типового кода приводит к усложнению процесса обновления: каждое обновление требует слияния (merge) всех доработок с новой версией, что может быть трудоемким и чреватым ошибками. Расширения же позволяют нам внедрять дополнительное поведение, сохраняя при этом типовую конфигурацию на стандартной поддержке, что значительно упрощает ее сопровождение и обновление.

Внедрение дополнительного поведения (Аспекты)

Подобно тому, как аспекты внедряют свою логику в основной код, расширения позволяют нам добавлять или модифицировать существующую логику в определенных "точках" конфигурации. Мы можем:

Это по сути и есть реализация принципа "аспектов" — мы инкапсулируем нашу дополнительную функциональность в рамках расширения и применяем ее к типовой конфигурации.

Точки соединения (Joinpoints) в 1С

В контексте 1С:Предприятия "точками соединения", где мы можем "вплетать" нашу аспектную логику с помощью расширений, являются:

  1. Обработчики событий форм: Это одни из наиболее часто используемых точек. Например, события ПриСозданииНаСервере, ПередЗаписьюНаСервере, ПослеЗаписиНаСервере, ПриЗакрытии для форм документов, справочников, отчетов. Мы можем добавить свою логику до или после выполнения стандартных действий.
  2. Обработчики событий модулей объектов: События ПередЗаписью, ПриЗаписи, ОбработкаПроведения для документов, ПередУдалением для объектов. Эти точки позволяют нам влиять на бизнес-логику на уровне данных.
  3. Обработчики событий команд: Мы можем добавить свой код при выполнении стандартных команд или создать новые команды.
  4. Переопределяемые процедуры и функции общих модулей: В некоторых типовых конфигурациях разработчики специально предусматривают процедуры и функции, которые предназначены для переопределения в расширениях. Их названия часто содержат суффиксы, указывающие на возможность доработки (например, _ПриИзменении, _ПередЗаписьюРасширения). Эти методы являются явными "точками расширения", предложенными разработчиками конфигурации.
  5. Подписки на события: В 1С есть механизм подписок на события, который позволяет реагировать на действия с объектами (запись, удаление, проведение и т.д.) для выбранных типов объектов. Это классический пример аспектно-подобного поведения, где мы "подписываемся" на определенные точки соединения, а наша функция-обработчик является "советом".

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

Сравнение типов советов АОП с возможностями 1С:Предприятия

Давайте подробнее рассмотрим, как различные типы советов АОП могут быть реализованы или имитированы с помощью механизмов расширений и подписок 1С:Предприятия.

Ключевое ограничение: отсутствие прямого аналога "Around" совета

Однако, как справедливо было отмечено в исходном обсуждении, 1С еще есть что дорабатывать в этой области, особенно если сравнивать с более развитыми АОП-фреймворками. Критическим отличием является отсутствие прямого, декларативного аналога "Around" совета, который давал бы полный контроль над вызовом метода.

Напомним, что совет типа "Around" предоставляет разработчику максимальный контроль над выполнением метода. Он позволяет не только выполнить логику до и после, но и:

В 1С, когда мы переопределяем процедуру или функцию в расширении (например, используя механизм "добавить" или "изменить" в общих модулях, или посредством подписок на события), мы обычно добавляем логику до или после стандартного выполнения. Но у нас нет возможности просто и декларативно заблокировать или полностью подменить основной вызов без написания сложной обертки или (что хуже) модификации самого метода-источника. Механизм подписок на события позволяет выполнять код до и после, и даже отменить стандартное действие (через параметр Отказ = Истина), но это не эквивалентно полному контролю, который предлагает "Around".

Для полной замены метода в 1С без изменения конфигурации приходится прибегать к следующим подходам:

  1. Создание собственного метода в расширении и перенаправление всех вызовов к нему, если это возможно.
  2. Использование флагов или параметров в типовой конфигурации, чтобы "отключить" стандартную логику, если такие флаги предусмотрены. Но это, как правило, требует поддержки со стороны разработчиков типовой конфигурации.

Отсутствие мощного "Around" совета означает, что разработчикам 1С приходится искать обходные пути, если требуется полностью контролировать или заменять выполнение стандартных методов. Это может привести к менее элегантным решениям или, в худшем случае, к необходимости частичного изменения типового кода, что является анти-паттерном для расширений.

Механизм плетения (Weaving) в 1С

Важно также рассмотреть, как происходит "плетение" аспектов в 1С. В платформе 1С:Предприятие плетение расширений происходит во время выполнения (Runtime weaving). Это означает, что:

  1. Расширения применяются к конфигурации при запуске информационной базы.
  2. Платформа динамически модифицирует объекты, формы, модули и другие элементы конфигурации, "встраивая" в них логику расширений.
  3. Если в расширении и в основной конфигурации есть пересекающиеся изменения (например, один и тот же обработчик события), то, как правило, применяется логика из расширения, или она дополняет стандартную (зависит от типа объекта и события). Это обеспечивает приоритет доработок в расширениях.

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

Преимущества использования расширений 1С с точки зрения АОП

Несмотря на некоторые ограничения, подход, реализуемый расширениями 1С, приносит значительные преимущества, которые мы можем ассоциировать с принципами АОП:

  1. Четкое разделение ответственности: Мы можем четко отделить сквозную функциональность (например, специфичные для конкретного клиента проверки, логирование бизнес-операций для аудита, интеграционные точки с внешними системами) от основной бизнес-логики типовой конфигурации. Это значительно улучшает читаемость и поддерживаемость кода.
  2. Улучшенная модульность и гранулярность доработок: Каждая сквозная задача или группа связанных изменений может быть инкапсулирована в отдельное расширение. Например, одно расширение может заниматься аудитом данных, другое — интеграцией с CRM, третье — специфичными для пользователя отчетами. Это упрощает управление, развертывание и, при необходимости, отключение отдельных доработок без воздействия на остальную систему.
  3. Простота сопровождения и обновлений: Поскольку расширения не изменяют непосредственно код типовой конфигурации, процесс обновления платформы и самой конфигурации значительно упрощается. Нам не нужно тратить ресурсы на трудоемкое слияние (merge) изменений после каждого обновления от вендора. Расширения работают как "накладки", которые автоматически применяются к новой версии.
  4. Переиспользование кода и логики: Аспекты, реализованные в расширениях (например, универсальный механизм аудита данных или общий подход к интеграции), могут быть переиспользованы в разных конфигурациях или в разных частях одной конфигурации. Это экономит время и ресурсы разработчиков.
  5. Чистота и ясность основного кода: Основной код типовой конфигурации остается "чистым" от специфичной для клиента логики. Это улучшает его читаемость, упрощает понимание стандартного функционала и позволяет быстрее разбираться в системе новым разработчикам.

Практические советы по применению принципов АОП при разработке расширений 1С

Осознанное применение принципов АОП может значительно улучшить качество наших решений, создаваемых с помощью расширений 1С. Вот несколько практических советов:

  1. Идентифицируйте сквозные задачи на ранних этапах: Прежде чем приступать к доработке, мы должны четко определить, какие задачи являются "сквозными" и могут быть вынесены в отдельный аспект (или группу связанных изменений в одном расширении). Это может быть:
    • Единое логирование для всех операций записи ключевых документов.
    • Дополнительные проверки прав пользователя, которые должны срабатывать перед открытием форм или выполнением команд.
    • Массовая модификация данных перед их сохранением в базе, например, автоматическое заполнение дополнительных реквизитов.
    • Интеграционные хуки, которые должны вызываться после определенных бизнес-событий.

    Выявление таких задач позволяет нам проектировать модульные и переиспользуемые расширения.

  2. Максимально используйте подписки на события для логики "Before" и "After": Подписки на события — это наш основной инструмент для внедрения аспектной логики в 1С. Они идеально подходят для реализации советов типов "Before" и "After". Для добавления дополнительной валидации перед записью документа, мы можем создать подписку на событие ПередЗаписью для всех нужных объектов. Для логирования после успешной операции — ПриЗаписи.

    Пример: Аудит изменения статуса документа.

    
    // В расширении:
    // Создаем ПодпискуНаСобытие с именем "АудитИзмененияСтатуса"
    // Источник: ДокументСсылка.ЗаказКлиента (или нужный тип объекта)
    // Событие: ПриЗаписи
    // Обработчик: ПодпискиНаСобытияРасширения.АудитИзмененияСтатусаПриЗаписи
            
    // В модуле ПодпискиНаСобытияРасширения:
    Процедура АудитИзмененияСтатусаПриЗаписи(Источник, Отказ) Экспорт
        Если ТипЗнч(Источник) = Тип("ДокументОбъект.ЗаказКлиента") Тогда
            // Предполагаем, что Статус - реквизит, и можем получить старое значение
            // (в реальности для аудита старых значений требуется отдельный механизм,
            // например, чтение из истории объекта или временное хранение)
            СтарыйСтатус = ДополнительныеСвойстваОбъекта.ПолучитьСтатусДоЗаписи(Источник.Ссылка); // Концептуально
            Если Источник.Модифицирован И Источник.Статус <> СтарыйСтатус Тогда 
                // Если статус изменился, записываем в наш журнал аудита
                МойЖурналАудита.ЗаписатьСобытие( // Концептуальный вызов
                    "ИзменениеСтатусаДокумента",
                    "Документ ЗаказКлиента " + Источник.Номер + " от " + Источник.Дата
                    + " изменил статус с " + СтарыйСтатус
                    + " на " + Источник.Статус
                );
            КонецЕсли;
        КонецЕсли;
    КонецПроцедуры
    

    В этом примере АудитИзмененияСтатусаПриЗаписи действует как "After" совет, срабатывающий после записи документа, для сквозной задачи аудита.

  3. Переопределяйте процедуры и функции общих модулей, предназначенные для расширения: Многие типовые конфигурации (особенно на БСП) предоставляют "заглушки" или специальные процедуры/функции в общих модулях, которые явно предназначены для переопределения в расширениях. Использование таких точек является наиболее "чистым" способом внедрения логики, так как эти "точки соединения" были предусмотрены разработчиками конфигурации. Мы должны активно искать и использовать эти возможности, когда это применимо.
  4. Активно модифицируйте формы для UI-аспектов: Для изменения поведения элементов форм и внедрения логики, связанной с пользовательским интерфейсом, используйте возможность модификации форм в расширениях. Мы можем добавлять новые элементы управления, изменять свойства существующих, а также добавлять свои обработчики событий к элементам формы (например, ПриИзменении, ПриНачалеРедактирования). Это по сути является внедрением логики "Before" или "After" для интерактивных действий пользователя.

    Пример: Добавление специальной кнопки на форму списка документов.

    
    // В расширении:
    // Открываем форму списка документов "РеализацияТоваровУслуг".
    // Добавляем новую команду "СформироватьОтчетПоВыделенным"
    // Добавляем кнопку для этой команды на командную панель формы.
            
    // В модуле формы расширения (для модифицированной формы):
    Процедура СформироватьОтчетПоВыделенным(Команда)
        Если Элементы.Список.ТекущаяСтрока = Неопределено Тогда
            Сообщить("Выберите документы для формирования отчета.");
            Возврат;
        КонецЕсли;
        // Получаем выделенные строки
        ВыделенныеСтроки = Новый Массив;
        Для Каждого СтрокаТЗ Из Элементы.Список.ВыделенныеСтроки Цикл
            ВыделенныеСтроки.Добавить(СтрокаТЗ.Ссылка);
        КонецЦикла;
                
        Если ВыделенныеСтроки.Количество() > 0 Тогда
            // Здесь вызываем наш отчетный модуль, передавая ему массив ссылок
            МойМодульОтчетовРасширения.СформироватьОтчет(ВыделенныеСтроки); // Концептуальный вызов
        Иначе
            Сообщить("Документы не выбраны.");
        КонецЕсли;
    КонецПроцедуры
    

    Эта процедура действует как "After" совет для действия пользователя (нажатия кнопки).

  5. Помните о "плетении" в рантайме: Понимание того, что расширения "плетутся" во время выполнения программы, помогает нам осознанно подходить к проектированию. Это означает, что изменения применяются динамически, и мы должны учитывать потенциальное влияние на производительность при создании большого количества или очень сложных аспектов. Также это объясняет приоритет расширений над основной конфигурацией при совпадении объектов.
  6. Соблюдайте принцип единственной ответственности: Каждое расширение или аспект (логическая группа функциональности в рамках одного расширения) должно отвечать за одну четко определенную сквозную задачу. Избегайте создания "мега-расширений", которые пытаются решить все проблемы сразу. Это позволит нам избежать монолитных расширений и сделает систему более управляемой, гибкой и простой в тестировании.

Заключение

В ходе нашего анализа мы выяснили, что технология расширений 1С, хоть и не является полноценной и всеобъемлющей реализацией Аспектно-ориентированного программирования в классическом смысле, тем не менее, воплощает многие из его ключевых принципов и предоставляет разработчикам мощные инструменты для работы со сквозными задачами. Возможность внедрения дополнительного поведения без изменения исходного кода конфигурации, четкое разделение сквозных задач, улучшенная модульность и простота сопровождения — все эти аспекты делают расширения 1С инструментом, глубоко укорененным в идеях АОП.

Понимание концепций АОП, таких как аспекты, точки соединения, советы и плетение, помогает нам осознанно и эффективно подходить к проектированию и разработке доработок в 1С. Оно позволяет нам создавать более чистые, модульные, легко поддерживаемые и масштабируемые решения, которые гармонично сосуществуют с типовой функциональностью. Да, платформа 1С еще имеет потенциал для развития, особенно в части предоставления более гранулированного контроля над вызовами методов, сопоставимого с "Around" советом в классическом АОП. Однако уже сейчас мы располагаем обширным арсеналом механизмов для эффективного использования принципов АОП. Продолжаем развиваться, изучать новые подходы и применять эти знания на практике, чтобы наши решения в 1С становились еще более совершенными и отвечали самым высоким стандартам разработки.

← На главную