Качество кода: Поведенческие паттерны проектирования

03.03.20

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

Поговорим про применение паттернов проектирования в разработке на 1С.

Вступление:

Здравствуйте, меня зовут Крючков Владимир. Мы продолжаем рассматривать вопросы связанные с разработкой хорошего кода и повышения качества программного обеспечения – мы рассмотрим паттерны проектирования.
Как всегда, сосредоточьтесь и отложите на 20-30 минут дела. 

Статью мы построили следующим образом: 

  • сначала расскажем немного про паттерны; 
  • потом обсудим 4 типа паттернов с примерами (всего будет приведено 11 примеров на 1С).

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

 

О чем речь? Паттерны проектирования …

 

Паттерны представляют определенный способ построения программного кода для решения часто встречающихся проблем проектирования.

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

Концепцию паттернов впервые описал Кристофер Александер в книге «Язык шаблонов. Города. Здания. Строительство».

Идея показалась привлекательной авторам Эриху Гамму, Ричарду Хелму, Ральфу Джонсону и Джону Влиссидесу, их принято называть «бандой четырёх» (Gang of Four). В 1995 году они написали книгу «Design Patterns: Elements of Reusable Object-Oriented Software» - это книга довольно сложна для неподготовленного читателя. Фактически с данной точки во времени начала развиваться теория и практика применения этого механизма. 

Всего выделяют 23 паттерна проектирования, но мы рассмотрим поведенческий тип паттернов, как наиболее хорошо проецируемый в русло 1С.

  • Наблюдатель
  • Посредник
  • Цепочка обязанностей
  • Итератор
  • Состояние
  • Снимок
  • Шаблонный метод
  • Команда
  • Посетитель
  • Стратегия

Плюсы и минусы использования паттернов

 

 
Плюсы:

  • Снижает сложность разработки
  • Облегчает коммуникацию между разработчиками
  • Снижает количество ошибок
  • Многократное использование
  • Возможность выбора оптимального варианта из набора

Минусы:

  • Слепое следование может привести к усложнению программы
  • Нет полной применимости в языке 1С
  • Приходится импровизировать
  • Нужно думать и понимать

 

I) Паттерн "Наблюдатель"
 

Создаёт механизм подписки, позволяющий одним объектам следить и реагировать на события, происходящие в других объектах.

Когда лучше всего применять?

  • Когда после изменения состояния одного объекта требуется что-то сделать в других, но вы не знаете наперёд, какие именно объекты должны отреагировать
  • Когда одни объекты должны наблюдать за другими, но только в определённых случаях

Реализация

В 1С для реализации данной задачи платформенно существуют следующие механизмы - это подписки на события и оповещения

Обработка подписка на события реализуется следующим образом:

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

2. Обработка оповещения. Это реализуется с помощью следующих вшитых в платформу процедур:

  • Используем обработчики оповещения в модулях объектов «Обработка оповещения», «Обработка выбора»
  • В коде при передачи данных используем функции - «Оповестить», «Оповестить о выборе».

 

Пример из жизни - Вы подписываетесь на рассылку новостей на сайте или форуме.

Пример 1. Получение данных из формы выбора


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

Первый вариант решения - плохой. Чтобы получить ответ на вопрос, мы из формы "смысл жизни" открываем модально форму "ответ на вопрос" и получаем ответ непосредственно обращаясь к реквизитам или переменным формы.

Второй вариант решения - правильный. Правильно применить шаблон наблюдатель для решения подобных задач.

Технически - это две платформенные процедуры "ОбработкаОповещения" и "Оповестить". Что происходит в этом случае:

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

 

Пример 2. Регистрация в план обмена 

 

Задачу описать можно следующим образом: требуется создать новый план обмена с регистрацией данных с помощью ПРО.

Технически - это использование механизма подписка на событие (метаданные "Подписки на события") для обработки событий записи выбранных справочников и документов. Т.е. мы обрабатываем события записи только тех документов и справочников, которые нас интересуют в этом плане обмена.

Реализация примера:

1. Создаем план обмена, выбираем состав реквизитов с признаком - авторегистрация отключена.

2. Создаем обработки подписки на события в формате "имя плана обмена"+Регистрация "тип данных". Сам обработчик выглядит следующим образом:

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

3. Создаем правила регистрации объектов (ПРО) и загружаем в механизм БСП.

 

Пример 3. Доработка конфигурации под новый регистр движений

 

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

Технически - это использование механизма подписка на событие (метаданные "Подписки на события") для обработки событий проведения выбранных коммерческих документов. Т.е. мы обрабатываем события только тех документов, которые нас интересуют для заполнения нашего оборотного регистра накопления.

Реализация:

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

2. Создаем новый регистр накопления "обороты по ценовым группам номенклатуры с клиентами"

3. Создаем подписки на события с типом событий "Обработка проведения" и "Обработка удаления проведения". В них добавляем все документы, которые содержат товары и суммы.

4. Добавляем общий модуль, в котором создаем функцию обработки проведения и удаления проведения. И указываем его в в качестве обработчика подписки на события.


Плюсы и минусы.

«+» Издатели не зависят от конкретных классов подписчиков и наоборот.
«+» Вы можете подписывать и отписывать получателей на лету.
«+» Реализует принцип открытости/закрытости.
 
«-» Подписчики оповещаются в случайном порядке.

 

II) Паттерн "Посредник"
 

Назначение

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

Реализации. Клиент-сервер

Менеджер проектов выполняет связь между программистами, аналитиками, таксировщиками, заказчиком.

 

Пример 4. Механизм расчетов с клиентами.

 

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

Реализация. Фактически состоит из:

  • регистра накопления «Расчеты с клиентами» - он является посредником;
  • коммерческие и финансовые документы в части формирования движений и источника данных - обращаются к посреднику для записи данных; 
  • финансовый контроль - обращается к посреднику для выполнения функции контроля отрицательных остатков;
  • отчеты по взаиморасчетам - обращаются к посреднику за данными.

В каждом документе в менеджере объекта есть функция «ТекстЗапросаТаблицаРасчетыСКлиентами», которая определяет текст запроса получения набора данных для помещения в регистр. Данные в объекте при проведении можно посмотреть в дополнительных свойствах -> таблица для движений-> «ТаблицаРасчетыСКлиентами».

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

 

Пример 5. Копирование табличной части между документами.

 

Передача данных и уведомление о наличии данных идет не от документа к документу, а через посредника - «буфер обмена». Механизм называется «Копирование строк». Ниже чуть подробнее рассмотрим его реализацию.

Реализация. Состоит из:

  1. Временное хранилище значений (тут физически хранятся данные таблицных частей) и общие модули -  «КопированиеСтрокКлиент», «КопированиеСтрокСервер». Это наш посредник и его обвязка. 
  2. Сами формы документов с кнопками "копировать" и "вставить".

Более подробно по реализации:

В общем модуле "КопированиеСтрокСервер" процедура «ПоместитьВыделенныеСтрокиВБуферОбмена» – помещает в буфер обмена, а «ПолучитьСтрокиИзБуфераОбмена» получает данные из буфера обмена.

Для получения информации о наличии данных в буфере обмена используется функция «ПустойБуферОбмена» модуля «ОбщегоНазначения». В коде модуля формы проверка осуществляется через процедуру «УстановитьДоступностьКомандБуфераОбмена».

Замечание. Опять наблюдаем разбрасывание кода по всей конфигурации. Согласитесь, что можно же было и сделать отдельный общий модуль работы с буфером. Проблема сильного связывания по факту (слабое связывание и высокая сопряженность (Low coupling and High Cohesion)). Теперь к примеру, если им захочется, к примеру, переименовать параметр сеанса «БуферОбмена», то придется искать и править код по всей конфигурации, а не в одном общем модуле.

При выполнении операции сохранения в открытом документе вызывается функция оповещения с событием «КопированиеСтрокВБуферОбмена», по выполнению, которой изменяется доступность команды вставить из буфера. 

Как вы заметили, то для информирования пользователя о помещении данных в буфер обмена используется паттерн наблюдатель.

 

Плюсы и минусы

«+» Устраняет зависимости между компонентами, позволяя повторно их использовать.
«+» Упрощает взаимодействие между компонентами.
«+» Централизует управление в одном месте.
 
«-» Посредник может сильно раздуться.


III) Паттерн "Цепочка обязанностей"

 

Назначение

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

Когда стоит применять? 

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

Реализация

В рамках 1С реализуется использованием операторов «Если», «ИначеЕсли», «Иначе» и «Тогда».

 

Пример из жизни: Вы звоните на поддержку и вас переадресуют с одной линии на другую

Пример 6. Сохранение отчета в определенном формате

 

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

Функция СохранитьОтчет(Данные,Формат="pdf") Экспорт
	
	Результат = Ложь;
	
	Если Формат = "pdf" Тогда
		Результат = СохранитьФорматPDF(Данные);
	ИначеЕсли Формат = "xlsx" Тогда
		Результат = СохранитьФорматXLSX(Данные);
	ИначеЕсли Формат = "cvs" Тогда
		Результат = СохранитьФорматCVS(Данные);
	ИначеЕсли Формат = "html" Тогда
		Результат = СохранитьФорматHTML(Данные);
	КонецЕсли;	
	
	Возврат Результат;
	
КонецФункции

Пример 7. Обработка сообщений при оповещении

 

Пример фрагмента кода взят из конфигурации ERP 2.4 модуля формы заказа клиента.

&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)

    Если ИмяСобытия = "ПолученыСообщения"
        И Параметр.ФормаВладелец = УникальныйИдентификатор Тогда
        ПолученыСообщения(Параметр.Сообщения);
    КонецЕсли;
    
    Если ИмяСобытия = "КопированиеСтрокВБуферОбмена" Тогда
        
        УстановитьДоступностьКомандБуфераОбменаНаКлиенте();
        
    КонецЕсли;
    
    Если ИмяСобытия = "ДобавлениеПартнераВСегмент"
        ИЛИ ИмяСобытия = "УдалениеПартнераИзСегмента" Тогда
        
        УстановитьВидимостьЗапретаОтгрузкиПартнеру();
    КонецЕсли;
    
    Если ИмяСобытия = "Закрытие_РедактированиеКомплекта"
        И Параметр.ФормаВладелец = УникальныйИдентификатор Тогда
        ПриОкончанииРедактированияНабора(Параметр.АдресВоВременномХранилище);
		ОбеспечениеКлиент.ЗаполнитьСлужебныеРеквизиты(Объект.Товары, ДатаОтгрузкиОбязательна, СкладОбязателен);
   КонецЕсли;

КонецПроцедуры

Пример 8. Обработка заполнения модуля документов

 

В данном примере выполняется обработка заполнения документа заказа клиента из конфигурации ERP 2.4.

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

	// .....

КонецПроцедуры

Пример 9. Контроль выполнения результатов проведения

 

В функции "ВыполнитьКонтрольРезультатовПроведения" общего модуля "ПроведениеСерверУТ" выполняется обработка проведения. Она состоит из двух частей. В первой части происходит добавление процедур контролей:

	// Контроль отрицательных остатков по товарам
	Если ЕстьИзмененияВТаблице(ДанныеТаблиц,"ДвиженияСвободныеОстаткиИзменение")
		Или ЕстьИзмененияВТаблице(ДанныеТаблиц,"ДвиженияГрафикОтгрузкиТоваровИзменение") Тогда

		ТекстЗапроса = ТекстЗапроса + ТекстЗапросаКонтрольОбеспечения(
			ЕстьИзмененияВТаблице(ДанныеТаблиц,"ДвиженияСвободныеОстаткиИзменение"),
			ЕстьИзмененияВТаблице(ДанныеТаблиц,"ДвиженияГрафикОтгрузкиТоваровИзменение"));

		МассивКонтролей.Добавить(Врег("ОбеспечениеВременнаяТаблица1"));
		МассивКонтролей.Добавить(Врег("ОбеспечениеВременнаяТаблица2"));
		МассивКонтролей.Добавить(Врег("Обеспечение"));

	КонецЕсли;

Далее происходит выполнение пакета запросов контроля и  выполняется обработка ошибок при их наличии:

//...
Для Каждого Результат Из МассивРезультатов Цикл

	Итератор = Итератор + 1;
	Если Результат.Пустой() Тогда
		Продолжить;
	КонецЕсли;
	ИмяКонтроля = МассивКонтролей[Итератор];

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

 

Плюсы и минусы


«+» Уменьшает зависимость между клиентом и обработчиками.
«+» Реализует принцип единственной обязанности.
«+» Реализует принцип открытости/закрытости.

 
«-» Запрос может остаться никем не обработанным.


IV) Паттерн "Итератор"
 

Назначение

  • Когда нужно выполнить обход без раскрытия структуры
  • Когда нужен единый интерфейс перебора элементов

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

  

Реализация

Пример 10. Обход коллекций табличных частей, деревьев, результатов запросов, коллекций, массивов.

 

К примеру, стоит задача получить список уникальных наименований данных загруженных в таблицу из документа Excel, то эту задачу можно выполнить следующим образом:

СписокРазличных = Новый Соответствие();

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

 

Примеры 11. В более классической записи при выполнении обхода результата запроса.

 

Выполняем обход данных полученных объединением двух таблиц - организаций и контрагентов для "нормализации" наименования. Запрос получен конструктором.

Запрос = Новый Запрос;
Запрос.Текст = 
	"ВЫБРАТЬ
	|	Контрагенты.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Контрагенты КАК Контрагенты
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	Организации.Ссылка
	|ИЗ
	|	Справочник.Организации КАК Организации";

РезультатЗапроса = Запрос.Выполнить();

ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
	СпрОбъект = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект();
	СпрОбъект.Наименование = НормализироватьНаименование(СпрОбъект.Наименование);
	СпрОбъект.Записать();
КонецЦикла;

 

Заключение. 

 

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

Также хочу порекомендовать очень хороший сайт по данной тематике «https://refactoring.guru». Тут нет примеров по 1С, зато есть хорошее и иллюстрированное объяснение.

 
 

См. также

Результаты ревью кода 1500+ решений каталога Инфостарт: наиболее частые ошибки разработчиков в коде

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

Поделюсь своим опытом аудита кода авторских продуктов с Infostart.ru как одним из элементов применения DevOps-практик внутри Инфостарт. Будет настоящий код, боевые скриншоты, внутренние мемы от команды ИТ-лаборатории Инфостарт и прочее мясо – все, что любят разработчики.

10.04.2024    5683    artbear    80    

75

Ниндзя-код

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

Предлагаю вашему вниманию советы мастеров древности. Программисты прошлого использовали их, чтобы заострить разум тех, кто после них будет поддерживать код. Гуру разработки при найме старательно ищут их применение в тестовых заданиях. Новички иногда используют их ещё лучше, чем матёрые ниндзя. Прочитайте их и решите, кто вы: ниндзя, новичок или, может быть, гуру? (Адаптация статьи "Ниндзя-код" из учебника JavaScript)

01.04.2024    2321    DrAku1a    15    

33

Практическое программирование: когда скорость важнее совершенства

Рефакторинг и качество кода Бесплатно (free)

В новом материале мы анализируем, как в программировании баланс между быстротой разработки и тщательной проработкой кода влияет на конечный продукт. Обсуждаем, почему иногда важнее сосредоточиться на скорости выполнения проекта, и когда можно позволить себе уступить в качестве ради достижения бизнес-целей.

01.04.2024    601    Prepod2003    6    

2

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

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

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

18.03.2024    1356    ZhokhovM    4    

4

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

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

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

18.03.2024    3014    ZhokhovM    4    

9

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

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

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

29.09.2023    2080    1CUnlimited    15    

23