Является ли подписка на события в 1С паттерном Наблюдатель (Observer)?

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

Разработка на платформе «1С:Предприятие 8» зачастую кажется обособленной от классического объектно-ориентированного программирования (ООП). Многие базовые паттерны проектирования, описанные знаменитой «Бандой четырех» (Gang of Four / GoF), в 1С имеют свою специфику или встроены непосредственно в ядро платформы. Сегодня мы детально разберем один из самых частых вопросов, возникающих на стыке академической теории программирования и реальной практики 1С: является ли механизм подписки на события паттерном «Наблюдатель» (Observer)?

В этой статье мы подробно проанализируем архитектурную суть подписок в 1С, сопоставим их с классическим паттерном Observer, выясним, почему эта связь ближе к шаблону Publish-Subscribe (Pub-Sub), и научимся реализовывать настоящий динамический объектный Observer на платформе 1С.

Теоретическая справка: что такое классический Observer?

Для начала вспомним классическое определение. Паттерн Наблюдатель (Observer) создает связь «один ко многим» между объектами таким образом, что при изменении состояния одного объекта (Субъекта / Издателя) все связанные с ним объекты (Наблюдатели / Подписчики) автоматически получают уведомление и обновляют свое состояние.

Ключевые признаки классического GoF-паттерна Observer:

  1. Динамичность: Подписка и отписка происходят непосредственно во время выполнения кода программы (runtime). Субъект предоставляет методы типа Attach(Observer) и Detach(Observer).
  2. Объектная зависимость: Наблюдатели передают ссылки на себя (конкретные экземпляры объектов) Субъекту. Наблюдатели обязательно должны реализовывать определенный интерфейс (например, иметь метод Update()).
  3. Прямая связь: Издатель лично хранит массив ссылок на своих подписчиков и осуществляет их обход в цикле для отправки уведомлений.

Теперь давайте спроецируем эти правила на реализацию подписок на события в экосистеме 1С.

Анализируем «Подписку на события» в 1С

Рассмотрим стандартный механизм метаданных ПодпискаНаСобытие в 1С. Например, мы хотим выполнять определенные действия при проведении любого документа. Мы создаем подписку в конфигураторе, указываем источник (документы) и обработчик (процедуру в общем модуле).

Давайте сопоставим этот механизм с классическим паттерном:

  1. Статичность против динамичности. В 1С мы жестко и декларативно описываем связь в конфигураторе. Во время выполнения кода (в runtime) мы не можем «на лету» отписать конкретный документ от этой подписки или динамически добавить нового подписчика средствами платформы. Связь задана жестко на уровне метаданных.
  2. Отсутствие экземпляров-наблюдателей. Обработчиком события в 1С выступает процедура общего модуля. Как справедливо заметили участники дискуссии, общий модуль в 1С не имеет состояния и является, по сути, аналогом утилитного класса со статическими методами в Java или C#. То есть мы передаем управление не конкретному объекту-наблюдателю, а статическому методу.
  3. Наличие Брокера (Платформы). Объект-источник (например, документ) вообще не знает о существовании подписчиков и не управляет списком рассылки. Эту задачу берет на себя сама платформа 1С. В классической терминологии это делает механизм ближе к паттерну Publish-Subscribe (Издатель-Подписчик) или концепции Шины событий (Event Bus). Платформа здесь выступает централизованным брокером сообщений.

Вывод: Декларативная «Подписка на события» в 1С — это не классический объектный Observer, а его архитектурная эволюция в сторону событийно-ориентированной архитектуры (EDA) и паттерна Pub-Sub. Это сделано намеренно для обеспечения слабой связанности (loose coupling) модулей системы.

Где в 1С живет настоящий динамический Observer?

Если в бизнес-логике на сервере платформа предлагает нам статический Pub-Sub, то на клиенте (в интерфейсной части) мы имеем дело с чистейшим классическим паттерном Наблюдатель. Рассмотрим эти инструменты подробнее.

1. Механизм «Оповестить» и «ОбработкаОповещения»

Это классический динамический Event Dispatcher / Observer. Схема работы выглядит следующим образом:

  1. Любая открытая форма на клиенте может выступить в роли Издателя, вызвав метод платформы Оповестить(ИмяСобытия, Параметры, Источник).
  2. Все остальные открытые в данный момент формы (Наблюдатели) автоматически ловят это событие, если в них определена стандартная процедура-обработчик ОбработкаОповещения(ИмяСобытия, Параметр, Источник).
  3. Состав получателей абсолютно динамический: как только пользователь открывает форму, она неявно регистрируется платформой в пуле наблюдателей. Как только форма закрывается — она исключается из рассылки.

2. Метод «ОповеститьОбИзменении»

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

3. Объект «ОписаниеОповещения» (NotifyDescription)

С переходом на асинхронную модель работы в управляемых формах повсеместно применяется объект ОписаниеОповещения. Хотя концептуально это ближе к механизму обратного вызова (Callback), это также является развитием идеи наблюдателя, когда вызывающий код заранее передает ссылку на процедуру, которая должна отреагировать на событие в будущем (например, при закрытии модального окна).

Реализуем классический объектный Observer на чистом 1С

Что делать, если перед нами стоит задача спроектировать сложный интерактивный интерфейс (например, Автоматизированное рабочее место — АРМ), где бизнес-логика должна быть строго отделена от визуального представления, а формы должны динамически подписываться на изменения модели данных?

Поскольку в 1С нет классов в привычном понимании ООП, роль «классов» и «объектов» в объектной модели 1С могут успешно выполнять Внешние обработки или обычные Обработки конфигурации.

Давайте пошагово реализуем классический паттерн Наблюдатель.

Шаг 1. Создаем интерфейс Наблюдателя

Создадим обработку с именем ШаблонНаблюдателя. В модуле объекта этой обработки определим экспортный метод, который будут вызывать наши издатели:


// Модуль объекта Обработка.ШаблонНаблюдателя

// Принимает уведомление от субъекта об изменении состояния
Процедура ОбновитьСостояние(Издатель, ДополнительныеПараметры) Экспорт
    
    // Конкретная логика реакции на событие реализуется в наследниках
    ВызватьИсключение "Метод должен быть переопределен в конкретном наблюдателе!";
    
КонецПроцедуры

Шаг 2. Создаем Издателя (Субъекта)

Создадим обработку Издатель. В модуле объекта этой обработки мы организуем динамический массив для хранения ссылок на наши объекты-наблюдатели и методы управления подпиской.


// Модуль объекта Обработка.Издатель

Перем СписокНаблюдателей;

// Инициализация коллекции подписчиков
Процедура Инициализировать() Экспорт
    СписокНаблюдателей = Новый Массив;
КонецПроцедуры

// Динамическое добавление наблюдателя
Процедура Подписать(Наблюдатель) Экспорт
    
    Если СписокНаблюдателей = Неопределено Тогда
        Инициализировать();
    КонецЕсли;
    
    // Проверяем, нет ли уже такого подписчика в массиве
    Если СписокНаблюдателей.Найти(Наблюдатель) = Неопределено Тогда
        СписокНаблюдателей.Добавить(Наблюдатель);
    КонецЕсли;
    
КонецПроцедуры

// Динамическое удаление наблюдателя
Процедура Отписать(Наблюдатель) Экспорт
    
    Если СписокНаблюдателей = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Индекс = СписокНаблюдателей.Найти(Наблюдатель);
    Если Индекс <> Неопределено Тогда
        СписокНаблюдателей.Удалить(Индекс);
    КонецЕсли;
    
КонецПроцедуры

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

Шаг 3. Пример практического использования в коде

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


// Пример использования паттерна в клиентском коде

// 1. Создаем объект-издатель
ОбъектИздатель = Обработки.Издатель.Создать();
ОбъектИздатель.Инициализировать();

// 2. Создаем конкретных наблюдателей (ими могут быть формы или другие обработки)
НаблюдательЛогирования = Обработки.НаблюдательЛог.Создать();
НаблюдательИнтерфейса = Обработки.НаблюдательЭкран.Создать();

// 3. Динамически подписываем их на события издателя
ОбъектИздатель.Подписать(НаблюдательЛогирования);
ОбъектИздатель.Подписать(НаблюдательИнтерфейса);

// 4. Происходит какое-то бизнес-событие в издателе
// Все подписчики автоматически получат уведомление и выполнят свой код
ОбъектИздатель.ОповеститьНаблюдателей("Данные успешно обновлены!");

// 5. При необходимости мы можем динамически отписать один из объектов
ОбъектИздатель.Отписать(НаблюдательЛогирования);

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

Резюме сравнения подходов

Для закрепления материала структурируем особенности реализации паттерна в 1С:

  1. Подписка на события (Метаданные): Это реализация паттерна Pub-Sub (Publish-Subscribe). Она статична, декларируется в конфигураторе, обрабатывается на сервере через общие модули. Идеальна для сквозного расширения логики проведения, записи или удаления документов без изменения их кода.
  2. Оповестить / ОбработкаОповещения (Платформа): Это встроенный динамический Observer на клиенте. Отлично подходит для синхронизации состояния открытых форм пользователей в реальном времени.
  3. Объектный Observer на Обработках: Это классический паттерн GoF, создаваемый программно разработчиком. Он обеспечивает максимальную гибкость и слабую связанность при проектировании сложных локальных алгоритмов и крупных модулей интерфейса.

Понимание этих тонкостей архитектуры позволяет 1С-разработчикам писать чистый, поддерживаемый код, успешно совмещая современные стандарты проектирования ПО с мощными возможностями платформы «1С:Предприятие».

← На главную