При проектировании алгоритмов валидации данных в 1С разработчики часто сталкиваются с дилеммой: как оформить код проверок? С одной стороны, можно использовать логические функции, возвращающие Истина или Ложь. С другой стороны, часто применяется процедурный подход с передачей входного параметра Отказ, изменяемого по ссылке. Разберем по шагам, в каких ситуациях каждый подход считается стандартом, какие подводные камни скрывает неявная мутация данных и как писать чистый, поддерживаемый код.
Если метод предназначен исключительно для проверки условия или валидации состояния данных и не производит никаких других действий, его следует оформлять как Функцию. Это прямо следует из общепринятого принципа CQS (разделение команд и запросов): метод должен либо выполнять действие (команда), либо возвращать данные без изменения состояния системы (запрос).
Посмотрим на пример правильной организации вызова:
Если НЕ ДанныеЗаполненыКорректно(ДокументОбъект) Тогда
Возврат;
КонецЕсли;
Рассмотрим преимущества такого подхода:
ПроверитьДанные(Отказ) неявно модифицирует переданную переменную, что затрудняет отладку и чтение кода. Функция же возвращает явный результат.Если НЕ ... Тогда, разработчик сразу понимает логику ветвления алгоритма без необходимости переходить внутрь процедуры.Истина/Ложь.Переменная Отказ широко используется в платформе 1С, однако у нее есть строго определенное назначение. Ее применение оправдано в двух ключевых сценариях:
ПередЗаписью, ОбработкаПроведения, ПриОткрытии и др.) изначально содержат системный параметр Отказ. Здесь задача платформы — получить сигнал об отмене действия.Проанализируем пример накопительного сбора ошибок:
Отказ = Ложь;
ПроверитьЗаполнениеШапки(Отказ, ЭтотОбъект);
ПроверитьТабличнуюЧасть(Отказ, ЭтотОбъект);
ПроверитьОстаткиНаСкладе(Отказ, ЭтотОбъект);
Если Отказ Тогда
Возврат;
КонецЕсли;
При использовании переменной Отказ необходимо строго соблюдать стандарты разработки платформы 1С:
Отказ категорически запрещено явно присваивать значение Ложь внутри вспомогательных методов. Если одна проверка уже установила флаг в значение Истина, последующая процедура не должна его затирать. Значение должно только накапливаться.Посмотрим на пример безопасной установки флага внутри процедуры:
Процедура ПроверитьТабличнуюЧасть(Отказ, ДокументОбъект) Экспорт
Если ДокументОбъект.Товары.Количество() = 0 Тогда
// Накапливаем флаг отказа, не сбрасывая его в Ложь
Отказ = Истина;
// Информируем пользователя о причине отказа
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = НСтр("ru = 'Табличная часть не может быть пустой!'");
Сообщение.Поле = "Товары";
Сообщение.УстановитьДанные(ДокументОбъект);
Сообщение.Сообщить();
КонецЕсли;
КонецПроцедуры
Отказ = Истина; обязательно должна сопровождаться формированием объекта СообщениеПользователю. Если отменить проведение или запись молча, платформа выдаст стандартную системную ошибку (например, «Не удалось записать документ»), не объясняя пользователю причину.&После платформенных событий вызов исключения платформы запрещает изменение параметра, отменяющего действие. Это еще одна причина избегать неконтролируемого использования входных параметров-флагов в прикладном коде.Если в результате проверки требуется вернуть не только факт успеха или ошибки, но и сопроводительную информацию (текст ошибки, код сбоя или вычисленные данные), ни булева функция, ни мутация переменной Отказ не являются оптимальными. В таких случаях стандартом считается возврат объекта структуры.
Разберем пример реализации через структуру:
Функция ПроверитьВозможностьОтгрузки(ЗаказКлиента) Экспорт
Результат = Новый Структура;
Результат.Вставить("Успех", Истина);
Результат.Вставить("ТекстОшибки", "");
Если ЗаказКлиента.СуммаДокумента <= 0 Тогда
Результат.Успех = Ложь;
Результат.ТекстОшибки = НСтр("ru = 'Сумма заказа должна быть больше нуля.'");
Возврат Результат;
КонецЕсли;
// Дополнительные проверки состояния взаиморасчетов
Возврат Результат;
КонецФункции
Посмотрим, как аккуратно выглядит обработка такого вызова в вызывающем коде:
РезультатПроверки = ПроверитьВозможностьОтгрузки(ТекущийЗаказ);
Если НЕ РезультатПроверки.Успех Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(РезультатПроверки.ТекстОшибки);
Возврат;
КонецЕсли;
Выясним, как поступать в ситуациях, когда продолжение работы алгоритма нарушит ссылочную целостность базы данных (например, при жестких блокировках в транзакциях проведения). В таких случаях использование флагов и ручной возврат через каждый уровень стека вызовов избыточны. Оптимальным решением служит вызов исключения:
Если Остаток < ТребуемоеКоличество Тогда
ВызватьИсключение НСтр("ru = 'Недостаточно остатка товара на складе для списания.'");
КонецЕсли;
Оператор ВызватьИсключение прерывает выполнение, автоматически откатывает открытую транзакцию и передает подробный текст ошибки в интерфейс или журнал регистрации.
Подведем практический итог для выбора архитектурного решения:
Истина / Ложь). Имя метода должно быть предикатом: ДанныеЗаполнены(), ПроверкаПройдена().Ложь.Неопределено в качестве признака отрицательного результата проверки: это значение должно означать лишь невозможность выполнения операции или отсутствие инициализированного значения.