Принципы ООП в 1С на примере реализации pattern Decorator

21.06.18

Разработка - Рефакторинг и качество кода

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

Скачать файлы

Наименование Файл Версия Размер
Принципы ООП в 1С на примере реализации pattern Decorator:
.zip 12,71Kb
5
.zip 12,71Kb 5 Скачать

 

Для начала ознакомимся с UML диаграммой шаблона decorator

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

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

 

Приступим к реализации. 

Например, перед нами стоит задача реализовать алгоритм поиска контрагента.

создаем обработку с экспортным методом в модуле объекта НайтиКонтрагента(ИНН, КПП); вызов из кода будет такой

Объект = Обработки.ПоискКонтрагента.Создать();
Контрагент = Объект.НайтиКонтрагента(ИНН, КПП); // Какой-то ИНН и какой-то КПП

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

 

Давайте подумаем, какую роль выполняет интерфейс в ООП? Как нам подсказывает вики 

устанавливают взаимные обязательства между элементами программной системы

Если своими словами, то объявленные методы в интерфейсе обязательно должны быть реализованы в каждом классе который реализует этот интерфейс.

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

Содержимое модуля объекта

 

Метод Имплементация - основной, он делает проверку на существования в реализующем классе необходимых методов (правда коряво эта проверка происходит), так же инициализирует значением реквизит РеализующийОбъект.

Метод НайтиКонтрагента - по сути редирект на метод объекта, это нужно, что бы можно было делать вызов метода, используя интерфейс.


В обработку ПоискКонтрагента добавляем реквизит БазовыйОбъект, тип составной (типы от которых может наследоваться этот класс) модуль объекта обработки добавляем методы Имплементация().

Функция Имплементация(ИнтерфейсИмя) Экспорт 
	Возврат Обработки[ИнтерфейсИмя].Создать().Имплементация(ЭтотОбъект);
КонецФункции

и Наследовать() 

Функция Наследовать(БазовыйОбъект) Экспорт 
	ЭтотОбъект.БазовыйОбъект = БазовыйОбъект;	
	Возврат ЭтотОбъект;
КонецФункции

 На текущем этапе БазовыйОбъект нам по большому счету не нужен, но давайте придерживаться одного шаблона, не важно наш класс наследуется от другого или нет, структура должна быть шаблонная.

В начале метода НайтиКонтрагента делаем проверку

Если ДанныеВерсии.Свойство("РежимПроверки") И ДанныеВерсии.РежимПроверки Тогда
  Возврат Неопределено;	
КонецЕсли;

Это нужно для метода ПроверитьСуществованиеМетодов (который у нас в модуле Интерфейсы). Ну нет у 1С нормального способа проверить существует ли метод в модуле объекта или нет.


Вот так изменится вызов метода НайтиКонтрагента

Данные = Новый Структура("ИНН, КПП", КакойтоИНН, КакойтоКПП); // Входящий параметр теперь один, структура.
Интерфейс = Обработки.ПоискКонтрагента.Создать().Имплементация("ПоискКонтрагентаИнтерфейс"); // ПоискКонтрагентаИнтерфейс - имя обработки интерфейса.
Контрагент = Интерфейс.НайтиКонтрагента(Данные); // Вызов через интерфейс


 

Зачем такие сложности скажите вы, зато теперь мой "интерфейс" могут имплементировать разные обработки разной структуры и разными методами, но мы 100% уверены, что у любой обработки есть нужный нам метод и мы можем вызвать его используя интерфейс.

На данном этапе мы реализовали эту часть диаграммы: 


Теперь реализуем сам декоратор.

Пусть наш декоратор расширяет алгоритм таким образом, что бы в случаи если не найден контрагент, он создавался.

Для этого создаем еще один класс обработку Декоратор1, копия обработки ПоискКонтрагента 

Единственное изменение, это доработанный метод НайтиКонтрагента(), доработан он таким образом:

Функция НайтиКонтрагента(Данные) Экспорт 
	Если ДанныеВерсии.Свойство("РежимПроверки") И ДанныеВерсии.РежимПроверки Тогда
		Возврат Неопределено;	
	КонецЕсли;

	Версия = БазовыйОбъект.НайтиКонтрагента(Данные); // Вызываем поиск базового объекта.
// Дополнение которое привносит декоратор.
	Если Не ЗначениеЗаполнено(Версия) Тогда
		Версия = СоздатьКонтрагента(ДанныеВерсии);	
	КонецЕсли;
	
	Возврат Версия;
КонецФункции
Функция СоздатьКонтрагента(ДанныеВерсии)
	// Создаем.	
	Сообщить("Создаем новый элемент справочника"); // Для демонстрации, что вызов есть.
КонецФункции

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

Вызов будет таким:

Данные = Новый Структура("ИНН, КПП", КакойтоИНН, КакойтоКПП); // Входящий параметр теперь один, структура.
Интерфейс = Обработки.ПоискСправочника.Создать().Имплементация("ПоискСправочникаИнтерфейс");
Декоратор1 = Обработки.Декоратор1.Создать()
								.Наследовать(Интерфейс)
								.Имплементация("ПоискСправочникаИнтерфейс");
Контрагент = Декоратор1.НайтиКонтрагента(Данные);

Обращаю внимание, что декоратор наследуется от интерфейса и новый объект так же имплементирует ПоискКонтрагентаИнтерфейс

Если мы захотим еще обернуть, то просто создаем копию декоратора 1 и реализуем в нем свои изменения, в моем примере это вывод еще одного сообщения (не забываем про типы реквизитов БазовыйОбъект и РеализующийОбъект)

Функция СоздатьКонтрагента(ДанныеВерсии)
	Сообщить("Что-то делаем"); // Для демонстрации, что вызов есть.
КонецФункции

Вызов изменится так: 

Данные = Новый Структура("ИНН, КПП", КакойтоИНН, КакойтоКПП); // Входящий параметр теперь один, структура.
Интерфейс = Обработки.ПоискСправочника.Создать().Имплементация("ПоискСправочникаИнтерфейс");
Декоратор1 = Обработки.Декоратор1.Создать()
								.Наследовать(Интерфейс)
								.Имплементация("ПоискСправочникаИнтерфейс");

Декоратор2 = Обработки.Декоратор2.Создать()
								.Наследовать(Декоратор1)
								.Имплементация("ПоискСправочникаИнтерфейс");
Контрагент  = Декоратор2.НайтиКонтрагента(Данные);

Декоратор1 - в данном примере так же тип интерфейс, вот тут наглядно представлена польза абстракции.

 

Вот результат 

хотя как мы видим вызов метода происходит один раз.


В итоге у нас вышла такая реализация

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

Спасибо за внимание!

ООП pattern Decorator GoF в

См. также

Когда понадобился новый оператор

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Когда понадобился новый оператор, но его нет в синтакс-помощнике, что делать?

18.03.2024    1162    ZhokhovM    2    

4

Когда разработчик платформы не добавил проверку препроцессоров

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Когда разработчик платформы решил пойти на кухню за кофе, а проверку препроцессоров не добавил, и вот тут-то и началось: "Что, опять все сломалось? Ну и кофе же я забыл сделать!".😅

18.03.2024    2695    ZhokhovM    4    

9

Реструктуризация - бесконечная история

Рефакторинг и качество кода Платформа 1С v8.3 Бесплатно (free)

При разработке программ требуемый функционал ставят на первое место, но есть еще и архитектура программы. На горизонте 5-10 лет она становится важнее функционала, который должен работать при масштабировании и росте данных. Реструктуризация 5 терабайтной базы 1С 8.2 в формат 1С 8.3, складывает весь пазл архитектурных просчетов, которые сделали ради функционала. Как это исправить? - для разработки правильной архитектуры, нужно всего лишь сместить фокус с функционала и подумать о «вечном».

29.09.2023    1910    1CUnlimited    15    

22

Чистый код. Мой взгляд на жизнь в макаронных джунглях. Часть 2

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Россия Бесплатно (free)

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

27.09.2023    6972    Lemmonbri    136    

36

Чистый код. Мой взгляд на жизнь в макаронных джунглях. Часть 1

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Россия Бесплатно (free)

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

19.09.2023    4354    Lemmonbri    16    

31

5 подходов при доработке конфигурации 1С, чтобы в будущем не было мучительно больно её обновлять

Архитектура Рефакторинг и качество кода Обновление 1С Платформа 1С v8.3 Бесплатно (free)

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

10.08.2023    9592    0    1c-izhtc    37    

21

Задача на ошибки и неоптимальности при проведении приходной накладной

Рефакторинг и качество кода Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Задачу эту дают на собеседованиях, видимо, те франчи, которые не в состоянии оценить человека по резюме и в ходе беседы. По идее задачи, подобные этой, должны давать начинающим студентам. Но дают всем подряд. Итак: мои 5 копеек. Критика приветствуется.

11.07.2023    2217    magic1s    32    

11
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. PerlAmutor 129 21.06.18 18:52 Сейчас в теме
Функция НайтиКонтрагента(Данные) Экспорт 
	Если ДанныеВерсии.Свойство("РежимПроверки") И ДанныеВерсии.РежимПроверки Тогда
		Возврат Неопределено;	
	КонецЕсли;


(0) Это можно немного улучшить, на мой взгляд, таким образом:

		Попытка
			 Выполнить(СтрШаблон("Объект.%1(, Истина)", Метод));
		Исключение
			 ОтсутствующиеМетоды.Добавить(Метод);
		КонецПопытки;
...
Функция НайтиКонтрагента(Данные = Неопределено, РежимПроверки = Ложь) Экспорт 
	Если РежимПроверки Тогда
		Возврат Неопределено;	
	КонецЕсли;
Показать
2. lazarenko 238 21.06.18 19:11 Сейчас в теме
(1) можно, только это ничего ж не меняет. Просто в моем варианте хорошо то, что параметр у метода один
3. PerlAmutor 129 21.06.18 19:22 Сейчас в теме
(2) Тоже верно. Тогда вопрос, тут ошибки нет случаем? А то параметр называется "Данные", а поиск свойства идет у "ДанныеВерсии":

Функция НайтиКонтрагента(Данные) Экспорт 
	Если ДанныеВерсии.Свойство("РежимПроверки") И ДанныеВерсии.РежимПроверки Тогда
		Возврат Неопределено;	
	КонецЕсли;
4. lazarenko 238 22.06.18 08:08 Сейчас в теме
(3) да это я ошибся когда в статью переносил, в приаттаченных обработках нор.
5. l1ike 16.07.18 07:52 Сейчас в теме
Ну раз уж интерфейсы в 1с появились, давайте уже и статические типы прикручивайте )))
Вы соответствие объекта интерфейсу когда проверять планируете? При старте программы или при создании объекта? Если при создании объекта, то выгода от интерфейсов, по моему, весьма сомнительна. А если при старте программы, тогда проще как в javascript транспиляторы писать.
6. s_vidyakin 63 11.01.19 18:08 Сейчас в теме
Данные = Новый Структура("ИНН, КПП", КакойтоИНН, КакойтоКПП); // Входящий параметр теперь один, структура.
Интерфейс = Обработки.ПоискСправочника.Создать().Имплементация("ПоискСправочникаИнтерфейс");
Декоратор1 = Обработки.Декоратор1.Создать().Наследовать(Интерфейс).Имплементация("ПоискСправочникаИнтерфейс");
Контрагент = Декоратор1.НайтиКонтрагента(Данные);

Не превращайте 1С в богомерзкую яву! ))
Оставьте свое сообщение