Как оптимизировать сложные условия с множественным поиском по коду в 1С:Предприятие 8

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

В процессе разработки в системе 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда автоматизированные системы проверки кода (линтеры, такие как SonarQube, 1С:EDT или встроенные проверки конфигурации) начинают выдавать замечания на казалось бы простой код. Одной из самых частых претензий является избыточная цикломатическая и когнитивная сложность условий. Рассмотрим типичный пример: когда нам нужно проверить, входит ли ссылка в определенный набор элементов, и мы начинаем загромождать конструкцию Если многочисленными операторами ИЛИ в сочетании с методом НайтиПоКоду().

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

В чем заключается проблема исходного кода?

Проанализируем классический пример неоптимального условия:


Если ВидНеисправности = Справочники.ВидНеисправности.НайтиПоКоду("10464")
    ИЛИ ВидНеисправности = Справочники.ВидНеисправности.НайтиПоКоду("10998")
    ИЛИ ВидНеисправности = Справочники.ВидНеисправности.НайтиПоКоду("10999")
    ИЛИ ВидНеисправности = Справочники.ВидНеисправности.НайтиПоКоду("10929")
    ИЛИ ВидНеисправности = Справочники.ВидНеисправности.НайтиПоКоду("10467") Тогда
    // Какая-то логика
КонецЕсли;

Рассмотрим причины, по которым линтер и опытные разработчики требуют исправить этот фрагмент:

  1. Множественные обращения к базе данных: Каждый вызов метода НайтиПоКоду() — это отдельный SQL-запрос к СУБД. В худшем случае (если совпадение произойдет на последнем элементе или не произойдет вовсе), система выполнит 5 последовательных запросов там, где можно было бы обойтись нулем или одним.
  2. Зависимость от данных (Hardcode): Коды элементов справочника могут быть изменены пользователем в режиме «1С:Предприятие». Как только бухгалтер или менеджер поменяет код «10464» на «10464-А», ваша логика перестанет работать без переписывания конфигурации.
  3. Низкая читаемость: Большое нагромождение ИЛИ усложняет восприятие алгоритма.
  4. Ошибки оформления: Линтеры часто ругаются на отсутствие пробелов (например, //Комментарий вместо // Комментарий), так как это нарушает стандарты оформления кода фирмы 1С, затрудняя автоматический разбор текстов модулей.

Рассмотрим по шагам, как можно улучшить этот код, двигаясь от простых решений к архитектурно правильным.

Решение №1: Использование коллекции Массив

Самый простой способ избавиться от множественных ИЛИ — собрать искомые значения в Массив и использовать метод Найти(). Однако, чтобы не делать лишних запросов к БД внутри условия, коды следует вынести за пределы проверки.

Проанализируем пример оптимизации:


СписокКодов = Новый Массив;
СписокКодов.Добавить("10464");
СписокКодов.Добавить("10998");
СписокКодов.Добавить("10999");
СписокКодов.Добавить("10929");
СписокКодов.Добавить("10467");

// Получаем код проверяемого объекта один раз, чтобы не обращаться к нему в цикле/условии
ТекущийКод = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВидНеисправности, "Код");

Если СписокКодов.Найти(ТекущийКод) <> Неопределено Тогда
    // Логика обработки
КонецЕсли;

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

Решение №2: Использование Соответствия (Hash-map)

Если элементов для сравнения много (не 5, а 50 или 100), поиск в массиве может замедлиться, так как метод Найти() выполняет линейный перебор. В этом случае эффективнее использовать объект Соответствие.

Выясним, почему это быстрее: поиск в Соответствие работает по принципу хеш-таблицы, и время поиска не зависит от количества элементов в списке (сложность $O(1)$ против $O(n)$ у массива).


КодыДляПроверки = Новый Соответствие;
КодыДляПроверки.Вставить("10464", Истина);
КодыДляПроверки.Вставить("10998", Истина);
// ... заполнение остальных

ТекущийКод = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВидНеисправности, "Код");

Если КодыДляПроверки[ТекущийКод] <> Неопределено Тогда
    // Элемент найден в списке
КонецЕсли;

Решение №3: Кэширование в структурах (для продвинутых)

Иногда разработчики пытаются использовать Структура, так как метод Свойство() работает очень быстро. Но здесь есть ловушка: ключи структуры должны соответствовать правилам именования идентификаторов (не могут начинаться с цифр и содержать пробелы).

Рассмотрим способ обхода этого ограничения, предложенный участниками обсуждения:


// Добавляем префикс "К", чтобы превратить числовой код в валидный идентификатор
КодыСтр = Новый Структура("К10464, К10998, К10999", Истина, Истина, Истина);

Если КодыСтр.Свойство("К" + ВидНеисправности.Код) Тогда
    // Обработка
КонецЕсли;

Важное замечание: Хотя этот метод быстр, он считается «хаком». Использование Соответствие или Массив является более стандартным подходом.

Решение №4: Архитектурный подход (Реквизит или Галка)

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

Правильным архитектурным решением будет:

  1. Добавить в справочник ВидНеисправности новый реквизит типа Булево (например, ТребуетОсобогоКонтроля) или реквизит с типом Перечисление.
  2. В режиме «Предприятие» расставить эти флаги у нужных элементов.
  3. В коде использовать простое и надежное условие:

Если ВидНеисправности.ТребуетОсобогоКонтроля Тогда
    // Логика
КонецЕсли;

Почему это лучше:

Решение №5: Использование предопределенных элементов

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


Если ВидНеисправности = Справочники.ВидыНеисправностей.ПоломкаХолодильника
    ИЛИ ВидНеисправности = Справочники.ВидыНеисправностей.КритическийСбой Тогда
    // Логика
КонецЕсли;

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

Итоги рефакторинга

Подводя итог, проанализируем стратегию выбора решения:

И не забывайте о мелочах: всегда ставьте пробел после символов комментария // . Это не просто прихоть «робота», а стандарт, который делает код профессиональным и удобным для поддержки всей командой.

← На главную