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