Вопрос о выборе между процедурой и функцией кажется тривиальным только на первый взгляд. Начинающие разработчики часто ориентируются на объем написанного кода, однако опытные специалисты знают, что за этим выбором стоят архитектурные принципы, удобство отладки и производительность системы. В этой статье мы проанализируем, в каких ситуациях следует использовать тот или иной программный метод, и разберем лучшие практики разработки на платформе 1С:Предприятие 8.
Рассмотрим фундаментальный принцип программирования — Command-Query Separation (CQS). Согласно этому подходу, каждый метод должен быть либо командой, выполняющей действие, либо запросом, возвращающим данные. Проанализируем, как это соотносится с синтаксисом 1С:
ПолучитьСкидкуНоменклатуры() не должен приводить к записи данных.Следование этому правилу делает ваш код предсказуемым. Когда другой разработчик видит вызов функции, он уверен, что данные не будут «тихо» изменены внутри метода.
Разберем пример, предложенный автором темы. Существует два способа получения скидки. Проанализируем их с точки зрения читаемости и надежности.
Вариант 1: Использование функции (Рекомендуется)
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ТекущаяСтрока.Скидка = ПолучитьСкидкуНоменклатуры(ТекущаяСтрока.Номенклатура);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьСкидкуНоменклатуры(Знач Номенклатура)
Возврат Номенклатура.ОсновнаяСкидка;
КонецФункции
Вариант 2: Изменение параметра в процедуре (Антипаттерн)
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
Номенклатура = Элементы.Товары.ТекущиеДанные.Номенклатура;
ПолучитьСкидкуНоменклатуры(Номенклатура);
// Теперь в переменной "Номенклатура" внезапно оказалось число (скидка)
Элементы.Товары.ТекущиеДанные.Skidka = Номенклатура;
КонецПроцедуры
&НаСервере
Процедура ПолучитьСкидкуНоменклатуры(Номенклатура)
Номенклатура = Номенклатура.ОсновнаяСкидка;
КонецПроцедуры
Выясним причину, по которой второй вариант считается плохим тоном. Во-первых, это неявное изменение типа переменной. В вызывающем коде мы ожидали ссылку на справочник, а получили число. Во-вторых, такой код крайне сложно читать: глядя на строку ПолучитьСкидкуНоменклатуры(Номенклатура), невозможно понять, что переменная Номенклатура будет изменена, если не заглянуть внутрь самой процедуры. Программисты называют это «побочным эффектом».
Иногда методу требуется вернуть не одно значение, а несколько (например, цену, процент скидки и вид налога). Вместо того чтобы создавать процедуру с пятью выходными параметрами, рассмотрим более изящное решение — использование объекта Структура.
Проанализируем пример универсальной функции, которая возвращает результат выполнения и описание ошибки:
Функция РассчитатьПараметрыПродажи(Номенклатура, Количество) Экспорт
Результат = Новый Структура;
Результат.Вставить("Успех", Ложь);
Результат.Вставить("Цена", 0);
Результат.Вставить("Сумма", 0);
Результат.Вставить("СообщениеОбОшибке", "");
Если Номенклатура.Пустая() Тогда
Результат.СообщениеОбОшибке = "Не выбрана номенклатура";
Возврат Результат;
КонецЕсли;
// Логика расчета...
Результат.Успех = Истина;
Результат.Цена = 100;
Результат.Сумма = 100 * Количество;
Возврат Результат;
КонецФункции
Этот подход позволяет легко расширять возвращаемые данные без изменения сигнатуры (количества параметров) метода. Вызывающий код становится чище, так как работа с результатом идет через обращение к свойствам структуры.
Важно помнить, что в 1С параметры в процедуры и функции передаются по ссылке. Это означает, что любое изменение параметра внутри метода отразится на исходной переменной. Чтобы защитить свои данные, всегда используйте ключевое слово Знач для входных параметров, которые не должны меняться.
Рассмотрим преимущества Знач:
Знач для сложных объектов (например, ТаблицаЗначений) может избавить платформу от необходимости передавать измененный объект обратно на клиент, если это не требуется.Для того чтобы выбор между процедурой и функцией был понятен с первого взгляда, придерживайтесь стандартов именования фирмы «1С»:
РассчитатьНалоги(), ЗаполнитьПоУмолчанию(), УдалитьВременныеФайлы()).ТекущаяЦена(), СформироватьСтруктуруОшибки(), ПолучитьМакетСервера()).ЭтоНовыйДокумент(), ДокументПроведен(), ПроверитьЗаполнениеФормы()).Несмотря на преимущества функций, процедуры остаются незаменимыми в следующих случаях:
ПриЗаписи, ПриИзменении, ОбработкаПроведения) реализованы как процедуры.ОписаниеОповещения обратный вызов (callback) всегда оформляется как процедура.Подведем итог: если вам нужно получить данные для дальнейшего использования в расчетах — пишите функцию. Если вам нужно выполнить действие или изменить состояние объекта — пишите процедуру. Избегайте возврата значений через параметры процедур, отдавая предпочтение возврату Структуры из функций. Это сделает ваш код профессиональным, понятным и легким в поддержке.