Использование структур для передачи параметров функций
Описана полезная технология для передачи контекста через структуры.
Допустим, мы выполняем некоторый функциональный блок, например проведение некоторого документа.
На входе у нас есть некоторые параметры - П1, П2, ... ПN.
Далее по мере продвижения выполнения кода на основе этих параметров вычисляются другие значения - З1, 32, ... ЗM.
Функциональный блок сделан в виде набора функций Ф1 ... ФK, которые вызываются друг из друга.
Параметры П и вычисленные значения З могут понадобиться в любых из этих функций.
Как добиться, чтоб значения П и З были доступны во всех нужных функциях, или в каждой из функций?
Можно завести глобальные переменные П1, П2, ... ПN и З1, 32, ... ЗM. Тогда проблема решена.
Но это плохое решение, т.к.:
- Глобальные переменные используются только на момент работы функции, в остальное время просто занимают память.
- Нарушается принцип изолированности функции, т.е. функция использует внешние данные.
- На сервере нельзя использовать глобальные переменные.
Второй вариант - во всех функциях передавать все необходимые значения П1, П2, ... ПN и З1, 32, ... ЗM.
Но это тоже плохое решение, т.к.:
- Нужно очень четко контролировать, какие переменные где нужны. Если что-то забыли, придется добавлять нужные переменные.
- Очень сильно растет стек, т.к. используется большое число параметров.
- Сложно добавить новый параметр, т.к. придется править все вызовы по передаче параметров.
Поэтому идеальное решение - использовать структуру для передачи контекста.
Обычно в своих решениях я даю ей наименование П.
При входе в блок я создаю структуру, в которой заполняю все исходные данные:
П = Новый Структура("П1, П2, ... ПN", П1, П2, ... ПN);
А затем вызываю первую функцию функционального блока.
Если какая-то из функций вычисляет значение, которое понадобится другой, то это значение просто добавляется в переменную контекста:
П.Вставить("ЗI", ЗI);
Таким образом, практически все функции блока имеют только один параметр - П.
Но не стоит увлекаться одной переменной контекста для всех участков кода. Вот пример, когда на участке лучше использовать отдельную переменную.
Пример с единой переменной:
Функция ОбойтиУровень(П)
Если П.Уровень = 0 Тогда
Возврат Неопределено;
КонецЕсли;
ТекУровень = П.Уровень;
П.Уровень = П.Уровень - 1;
ТекУровень = П.Уровень;
ОбойтиУровень(П);
П.Уровень = ТекУровень;
ОбработатьУровень(П.Уровень);
КонецФункции
Пример с дополнительной переменной выглядит красивее:
Функция ОбойтиУровень(П, Уровень)
Если Уровень = 0 Тогда
Возврат Неопределено;
КонецЕсли;
ОбойтиУровень(П, Уровень - 1);
ОбработатьУровень(П, Уровень);
КонецФункции
Конечно, такие нюансы обычно возникают в рекурсивных функциях. Следует придерживаться правила, что в контекст включаются только те переменные и значения, которые рассчитываются только один раз по ходу выполнения функционального блока, или, если вычисляются многократно, то старое значение очищается и вычисляется новое, а не происходит никаких модификаций значения (например инкремента).
У такого метода есть один плюс - на выходе из функционального блока в отладчике или для целей отладки в 1С можно получить все промежуточные результаты.
Если посмотреть код типовых (например, процедуры партионного учета), то видно, что структуры используются для агрегирования множества мелких значений (сама жизнь заставляет так поступать), иначе бы у процедур было по 20-30 параметров. Но объединение переменных в единый контекст пока еще не осуществляется, хотя переменные и путешествуют вместе по всему блоку проведения и расчета партий. Поэтому чтобы добавить свои промежуточные данные, приходится искать, в какую из имеющихся структур можно их включить.
Дополнительный бонус, который мы получаем при таком подходе - возможность кэшировать значения. Т.е. можно наиболее часто используемые значения закэшировать в отдельной ветке структуры, назвать ее Кэши. Такой подход позволяет резко увеличить производительность функционального блока.