Как реализовать наследование логики объектов и динамически отслеживать изменения данных в управляемых формах 1С?

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

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

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

Решение задачи наследования и общей логики для объектов метаданных (справочников и документов)

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

Способ 1. Использование расширений конфигурации и перехват методов

Механизм расширений позволяет гибко дорабатывать логику типовых или самописных конфигураций без изменения основного контура. Мы можем использовать аннотации &Перед, &После и &Вместо для перехвата стандартных процедур и функций модулей объектов и менеджеров.

При использовании аннотации &Вместо крайне важно вызывать встроенную функцию ПродолжитьВызов(). Это гарантирует, что цепочка вызовов других расширений не будет прервана, и система продолжит работать стабильно.

Посмотрим на пример перехвата процедуры записи документа в расширении:


&Вместо("ПередЗаписью")
Процедура Расш_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Наша общая логика, выполняемая перед записью любого расширяемого документа
    ВыполнитьОбщуюПроверкуПрав(ЭтотОбъект);
    
    // Передача управления стандартному обработчику и другим расширениям
    ПродолжитьВызов(Отказ, РежимЗаписи, РежимПроведения);
КонецПроцедуры

Способ 2. Использование определяемых типов и подписок на события

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

  1. Создаем в конфигураторе новый Определяемый тип (например, ОпределяемыйТип.ОбъектыСКонтролемДоступа) и включаем в его состав все необходимые документы и справочники.
  2. Создаем Подписку на событие (например, ПередЗаписьюОбъектаКонтроля). В качестве источника указываем наш созданный определяемый тип, а в качестве события выбираем ПередЗаписью.
  3. Пишем единый обработчик события в общем модуле. Данный код будет автоматически запускаться для каждого объекта, входящего в состав определяемого типа.

Проанализируем пример такого обработчика в общем модуле:


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

Способ 3. Имитация полиморфизма через общие модули (паттерн «Стратегия»)

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


Процедура ЗаполнитьРеквизитыОбъекта(Объект) Экспорт
    МетаданныеОбъекта = Объект.Метаданные();
    // Проверяем наличие стандартного реквизита "Автор" у переданного объекта
    Если МетаданныеОбъекта.Реквизиты.Найти("Автор") <> Неопределено Тогда
        Объект.Автор = ПараметрыСеанса.ТекущийПользователь;
    КонецЕсли;
КонецПроцедуры

Решение задачи отслеживания изменений данных в управляемых формах (УФ)

В обычных формах существовал удобный метод ПодключитьОбработчикИзмененияДанных, который позволял автоматически отслеживать любые интерактивные изменения. В управляемых формах этот метод не поддерживается из-за особенностей клиент-серверной архитектуры: постоянный мониторинг каждого действия пользователя на клиенте привел бы к избыточному трафику и снижению производительности.

Разберем три эффективных способа решения этой проблемы в управляемом приложении.

Способ 1. Использование свойства «Модифицированность» формы

Если нам нужно просто понять, изменил ли пользователь какие-либо данные на форме перед её закрытием или сохранением, мы можем использовать стандартное свойство ЭтаФорма.Модифицированность (тип Булево). Оно автоматически взводится платформой в значение Истина при любом интерактивном изменении данных.

Способ 2. Управление свойством «ИзменяетДанные» элементов формы

У каждого интерактивного элемента (например, поля ввода) есть свойство ИзменяетДанные. Если установить его в значение Истина, любое изменение значения пользователем в этом поле приведет к автоматическому изменению флага модифицированности всей формы.

Способ 3. Программное динамическое подключение обработчиков событий

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

Рассмотрим, как это реализовать по шагам:

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

Посмотрим на пример кода, реализующий этот алгоритм:


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

&НаКлиенте
Процедура Подключаемый_ПриИзмененииДанных(Элемент)
    // В параметре "Элемент" платформа передает объект элемента, который был изменен
    ИмяИзмененногоРеквизита = Элемент.ПутьКДанным;
    
    // Вызываем универсальный алгоритм логирования или валидации изменений
    ПоказатьОповещениеПользователя("Изменение данных",, "Пользователь изменил реквизит: " + ИмяИзмененногоРеквизита);
КонецПроцедуры

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

← На главную