Использование функций вместо процедур в 1С: подмена понятий или эффективная методология разработки?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

Суть проблемы: процедуры против функций

Для начала вспомним классическое определение синтаксиса встроенного языка «1С»:

Приверженцы «чистого функционального подхода» утверждают, что разделение на процедуры и функции является искусственным и устаревшим наследием ранних языков программирования. В современных языках (таких как Kotlin, Scala, Haskell или Go) любое объявление метода фактически является функцией, которая возвращает значение (даже если это пустой тип void или кортеж из нескольких элементов). Посмотрим, какие реальные практические выгоды и проблемы несет этот подход применительно к платформе «1С:Предприятие».

Аргумент №1: Удобство отладки через табло (Shift+F9)

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

Вы остановились на точке останова внутри сложного алгоритма и хотите проверить, как отработает сопутствующий метод. Если этот метод объявлен как Процедура, вы не сможете вызвать его в табло отладки (сочетание клавиш Shift+F9), поскольку процедура не возвращает значения, и платформа выдаст ошибку вычисления выражения. Если же метод объявлен как Функция, вы можете легко запустить его в табло, передать нужные параметры и сразу же визуально оценить результат вычислений (или возвращаемую структуру ошибок), не прерывая сеанс отладки.

Аргумент №2: Устойчивость к рефакторингу и эволюция кода

Рассмотрим жизненный цикл разработки. Очень часто метод пишется как простое действие:


Процедура РассчитатьЗадолженность(Контрагент)
    // Вычисления и запись в базу данных...
КонецПроцедуры

Через некоторое время бизнес-требования меняются. Теперь вызывающему коду необходимо знать, успешно ли прошел расчет задолженности, возникли ли ошибки блокировки данных или какова итоговая сумма. Если мы использовали процедуру, у нас есть два пути:

  1. Передать параметр по ссылке (неявный возврат): добавить в процедуру новый параметр вроде Отказ или Результат и менять его внутри процедуры. Это ухудшает читаемость кода, так как вызывающий код вынужден инициализировать переменные перед вызовом.
  2. Переписать процедуру в функцию: изменить ключевое слово Процедура на Функция, добавить оператор Возврат и обойти все места вызова этого метода в конфигурации, чтобы скорректировать синтаксис.

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


// Старый вызов продолжает работать:
РассчитатьЗадолженность(ТекущийКонтрагент);

// Новый вызов использует возвращаемое значение:
Если Не РассчитатьЗадолженность(ТекущийКонтрагент) Тогда
    ВызватьИсключение "Ошибка расчета!";
КонецЕсли;

Шаблон проектирования: Безопасный возврат результата через структуру

Вместо хаотичного возврата простых типов (Истина/Ложь, Неопределено) профессиональные разработчики используют стандартизированные структуры возврата. Давайте разберем по шагам создание такого универсального шаблона.

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


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

Преимущества такого шаблона очевидны:

Концепция CQS (Command-Query Separation) и стандарты 1С

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

Согласно принципу CQS, метод должен быть либо Командой (выполнять действие, менять состояние системы и ничего не возвращать — то есть быть Процедурой), либо Запросом (возвращать вычисленное значение без изменения состояния базы данных — то есть быть чистой Функцией). Смешивание этих подходов («функции-мутанты», которые пишут данные в регистры и одновременно возвращают отчетные структуры) затрудняет понимание логики программы и усложняет модульное тестирование.

Стандарты именования «1С» также строго разграничивают эти понятия:

Если мы называем метод глаголом (например, РассчитатьСкидки()), но объявляем его как функцию, мы заставляем будущего разработчика гадать: выполняет ли этот метод действие, возвращает ли он значение или делает и то, и другое одновременно?

Особые случаи: Перехват ошибок и валидация данных

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


Функция ГлавноеСвойствоОбъекта(Объект) Экспорт
    
    Результат = Неопределено;
    
    Если Объект.Пол = Перечисления.ПолФизическихЛиц.Мужской Тогда
        Результат = "Сила";
    ИначеЕсли Объект.Пол = Перечисления.ПолФизическихЛиц.Женский Тогда
        Результат = "Красота";
    Иначе
        // Если тип данных некорректен, "замалчивать" ошибку вредно.
        // Вызываем явное исключение для прерывания неверного алгоритма.
        ВызватьИсключение "Неизвестный или незаполненный пол объекта: " + Строка(Объект);
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

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

Влияние на расширения конфигурации (Аннотации &Перед, &После, &Вместо)

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

В современной разработке на платформе «1С:Предприятие 8.3» активно используются расширения конфигурации для адаптации типовых решений без снятия их с поддержки. То, как вы объявили исходный метод (как процедуру или как функцию), напрямую влияет на удобство его доработки в расширении:

Резюме и рекомендации

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

Тотальный отказ от процедур в пользу функций является избыточной мерой («серебряной пулей»), которая решает одни проблемы (удобство отладки и рефакторинга), но порождает другие (нарушение стандартов именования, усложнение разработки расширений, ухудшение читаемости кода). Наиболее эффективным является сбалансированный подход.

Рекомендуется придерживаться следующих правил:

  1. Используйте Процедуры для классических обработчиков событий (при изменении реквизитов формы, при проведении документов, при записи объектов), где важен сам факт выполнения действий и не требуется возвращать данные назад по стеку вызовов.
  2. Используйте Функции для любых расчетных модулей, запросов к базе данных, проверок заполнения и получения аналитических показателей.
  3. Если в методе-действии (бывшей процедуре) высока вероятность возникновения ошибок, которые необходимо программно обработать на уровень выше — смело преобразуйте его в Функцию, возвращающую структуру статуса выполнения (как в рассмотренном шаблоне безопасного возврата).
  4. Никогда не оставляйте функцию без оператора Возврат. Да, платформа «1С» не выдаст ошибку компиляции и вернет Неопределено по умолчанию, но инструменты статического анализа кода (такие как EDT или SonarQube) будут ругаться на такой код, снижая показатели качества вашего проекта.
← На главную