Часто разработчики, имеющие опыт программирования на объектно-ориентированных языках, сталкиваются со спецификой платформы 1С:Предприятие и пытаются найти способы реализации классического наследования (ООП) на уровне метаданных. Иногда это приводит к идеям низкоуровневой модификации платформы, подмене DLL или использованию сложных внешних компонент. Однако подобные методы нарушают лицензионное соглашение и ставят под угрозу стабильность системы.
В этой статье мы подробно разберем, как решить эти задачи штатными, безопасными и поддерживаемыми методами платформы 1С. Мы рассмотрим альтернативные паттерны для организации общей логики объектов и разберем способы динамического отслеживания изменений в управляемых формах.
В платформе 1С отсутствует поддержка классического ООП на уровне метаданных. Вы не можете создать абстрактный документ-родитель и унаследовать от него другие документы с автоматическим наследованием реквизитов, табличных частей и программных модулей. Тем не менее, существуют эффективные архитектурные паттерны, позволяющие полностью исключить дублирование кода.
Способ 1. Использование расширений конфигурации и перехват методов
Механизм расширений позволяет гибко дорабатывать логику типовых или самописных конфигураций без изменения основного контура. Мы можем использовать аннотации &Перед, &После и &Вместо для перехвата стандартных процедур и функций модулей объектов и менеджеров.
При использовании аннотации &Вместо крайне важно вызывать встроенную функцию ПродолжитьВызов(). Это гарантирует, что цепочка вызовов других расширений не будет прервана, и система продолжит работать стабильно.
Посмотрим на пример перехвата процедуры записи документа в расширении:
&Вместо("ПередЗаписью")
Процедура Расш_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Наша общая логика, выполняемая перед записью любого расширяемого документа
ВыполнитьОбщуюПроверкуПрав(ЭтотОбъект);
// Передача управления стандартному обработчику и другим расширениям
ПродолжитьВызов(Отказ, РежимЗаписи, РежимПроведения);
КонецПроцедуры
Способ 2. Использование определяемых типов и подписок на события
Это самый мощный штатный механизм платформы для реализации сквозной бизнес-логики. Если нам нужно внедрить единую логику проверки прав, валидации или заполнения реквизитов для множества справочников или документов, мы выполняем следующие шаги:
ОпределяемыйТип.ОбъектыСКонтролемДоступа) и включаем в его состав все необходимые документы и справочники.ПередЗаписьюОбъектаКонтроля). В качестве источника указываем наш созданный определяемый тип, а в качестве события выбираем ПередЗаписью.Проанализируем пример такого обработчика в общем модуле:
Процедура ПередЗаписьюОбъектаКонтроляПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Параметр "Источник" содержит сам записываемый объект (документ или справочник)
Если Не Источник.ОбменДанными.Загрузка Тогда
// Выполняем единую проверку заполнения или прав доступа
Если Не ПроверитьПраваПользователяНаОбъект(Источник) Тогда
Отказ = Истина;
ВызватьИсключение "Недостаточно прав для записи объекта!";
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Способ 3. Имитация полиморфизма через общие модули (паттерн «Стратегия»)
Если нам необходимо обрабатывать различные объекты универсальным образом, всю общую логику мы можем вынести в экспортные процедуры общих модулей. Передавая в качестве параметра сам объект ЭтотОбъект, мы можем динамически анализировать его метаданные и структуру:
Процедура ЗаполнитьРеквизитыОбъекта(Объект) Экспорт
МетаданныеОбъекта = Объект.Метаданные();
// Проверяем наличие стандартного реквизита "Автор" у переданного объекта
Если МетаданныеОбъекта.Реквизиты.Найти("Автор") <> Неопределено Тогда
Объект.Автор = ПараметрыСеанса.ТекущийПользователь;
КонецЕсли;
КонецПроцедуры
В обычных формах существовал удобный метод ПодключитьОбработчикИзмененияДанных, который позволял автоматически отслеживать любые интерактивные изменения. В управляемых формах этот метод не поддерживается из-за особенностей клиент-серверной архитектуры: постоянный мониторинг каждого действия пользователя на клиенте привел бы к избыточному трафику и снижению производительности.
Разберем три эффективных способа решения этой проблемы в управляемом приложении.
Способ 1. Использование свойства «Модифицированность» формы
Если нам нужно просто понять, изменил ли пользователь какие-либо данные на форме перед её закрытием или сохранением, мы можем использовать стандартное свойство ЭтаФорма.Модифицированность (тип Булево). Оно автоматически взводится платформой в значение Истина при любом интерактивном изменении данных.
Способ 2. Управление свойством «ИзменяетДанные» элементов формы
У каждого интерактивного элемента (например, поля ввода) есть свойство ИзменяетДанные. Если установить его в значение Истина, любое изменение значения пользователем в этом поле приведет к автоматическому изменению флага модифицированности всей формы.
Способ 3. Программное динамическое подключение обработчиков событий
Если нам необходимо реагировать на изменение конкретных полей формы без ручной привязки события ПриИзменении к каждому элементу в конфигураторе, мы можем подключить обработчики программно при создании формы.
Рассмотрим, как это реализовать по шагам:
ПриСозданииНаСервере выполняем обход коллекции элементов формы.УстановитьДействие, связывая событие ПриИзменении с единой процедурой модуля формы.Посмотрим на пример кода, реализующий этот алгоритм:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Динамически обходим все элементы формы
Для Каждого Элемент Из ЭтаФорма.Элементы Цикл
// Проверяем, является ли элемент полем ввода
Если ТипЗнч(Элемент) = Тип("ПолеФормы") И Элемент.Вид = ВидПоляФормы.ПолеВвода Тогда
// Подключаем наш общий клиентский обработчик к событию "ПриИзменении"
Элемент.УстановитьДействие("ПриИзменении", "Подключаемый_ПриИзмененииДанных");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура Подключаемый_ПриИзмененииДанных(Элемент)
// В параметре "Элемент" платформа передает объект элемента, который был изменен
ИмяИзмененногоРеквизита = Элемент.ПутьКДанным;
// Вызываем универсальный алгоритм логирования или валидации изменений
ПоказатьОповещениеПользователя("Изменение данных",, "Пользователь изменил реквизит: " + ИмяИзмененногоРеквизита);
КонецПроцедуры
Используя эти стандартные подходы, мы сохраняем чистоту кода, обеспечиваем простоту обновления конфигурации и полностью соответствуем стандартам разработки фирмы «1С», избегая опасных манипуляций с бинарными файлами платформы.