Рефакторинг кода в 1С: как превратить рабочий код в качественный и оптимальный?

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

В среде разработчиков 1С часто возникает дискуссия: что важнее — быстро написать рабочий код («лишь бы работало») или сразу тратить время на архитектурную красоту? И что на самом деле скрывается за понятием «красота кода»? Это не просто эстетика, а конкретные метрики производительности, поддерживаемости и читаемости. Рассмотрим подробнее, какие технические приемы делают код «красивым» с точки зрения платформы 1С:Предприятие, разберем опасные антипаттерны и способы их устранения.

Что такое рефакторинг и зачем он нужен?

Рефакторинг — это процесс изменения внутренней структуры программы без изменения её внешнего поведения. Цель состоит не в исправлении ошибок, а в упрощении понимания кода и удешевлении его дальнейшей поддержки. В обсуждении мы выяснили, что часто под рефакторингом понимают превращение «спагетти-кода» в структурированные модули.

Рассмотрим ключевые аспекты, которые отличают плохой код от хорошего в контексте 1С:

  1. Читаемость: Код должен быть понятен любому разработчику среднего уровня, а не только автору в момент написания.
  2. Производительность: «Красивый» код в 1С часто означает код, который не делает лишних запросов к базе данных (БД).
  3. Архитектурная чистота: Соблюдение стандартов разработки и принципов нормализации (или осознанной денормализации).

Проблема разыменования через точку: Антипаттерн и его решение

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

Рассмотрим пример функции, которая возвращает страну происхождения из заказа:


Функция СтранаПроисхождения(ЗаказКлиента)
    Возврат ЗаказКлиента.Контрагент.ГоловнойКонтрагент.ЮридическийАдрес.Страна;
КонецФункции

На первый взгляд код выглядит «красиво» и лаконично. Однако с технической точки зрения этот код является неоптимальным и потенциально опасным. Разберем почему.

Проблема 1: Производительность объектной модели

Когда вы обращаетесь к реквизиту ссылочного типа через точку (например, ЗаказКлиента.Контрагент), платформа 1С считывает объект целиком (если он еще не в кэше). В приведенном примере происходит последовательное чтение четырех объектов:

  1. Сам документ ЗаказКлиента (если передана ссылка).
  2. Справочник Контрагенты.
  3. Справочник Контрагенты (Головной).
  4. Справочник, хранящий ЮридическийАдрес (если это ссылочный тип).

Ради получения одного значения (Страна) мы загружаем в память четыре тяжелых объекта со всеми их табличными частями. Это создает избыточную нагрузку на сервер приложений и сеть.

Проблема 2: Неявные соединения в запросах

Если подобная конструкция встречается внутри текста запроса, ситуация становится еще хуже:


ВЫБРАТЬ
    Заказ.Контрагент.ГоловнойКонтрагент.ЮридическийАдрес.Страна
ИЗ
    Документ.ЗаказКлиента КАК Заказ

Каждая точка в полях запроса трансформируется СУБД (SQL Server или PostgreSQL) в левое соединение (LEFT JOIN). Цепочка из 4 точек создаст 4 вложенных соединения. На больших объемах данных это может привести к неоптимальному плану выполнения запроса и существенному замедлению работы системы.

Проблема 3: Обработка пустых ссылок (NullReference)

Код вида Объект.А.Б.В крайне неустойчив. Если у контрагента не заполнен ГоловнойКонтрагент, то обращение к .ЮридическийАдрес вызовет ошибку времени выполнения (попытка получения свойства у Неопределено или пустой ссылки). В запросах это может привести к значению NULL, которое нужно обрабатывать отдельно.

Правильное решение: Использование явных запросов

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

Рассмотрим вариант рефакторинга с использованием запроса:


Функция ПолучитьСтрануЗаказа(ЗаказКлиента) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ЕСТЬNULL(ЗаказКлиента.Контрагент.ГоловнойКонтрагент.ЮридическийАдрес.Страна, ЗНАЧЕНИЕ(Справочник.СтраныМира.ПустаяСсылка)) как Страна
    |ИЗ
    |    Документ.ЗаказКлиента КАК ЗаказКлиента
    |ГДЕ
    |    ЗаказКлиента.Ссылка = &ЗаказКлиента";
    
    Запрос.УстановитьПараметр("ЗаказКлиента", ЗаказКлиента);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    Если Не РезультатЗапроса.Пустой() Тогда 
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Страна;
    Иначе 
        // Обработка ситуации, когда заказ не найден или ссылка битая
        Возврат Справочники.СтраныМира.ПустаяСсылка();
    КонецЕсли;

КонецФункции

Важные замечания к этому коду:

  1. Мы используем ЕСТЬNULL, чтобы гарантированно вернуть значение правильного типа, даже если цепочка данных прервалась (например, нет головного контрагента).
  2. Даже в этом запросе сохраняется проблема неявных соединений (точек). Для высоконагруженных систем идеальным вариантом (и стандартом 1С) является явное описание соединений через ЛЕВОЕ СОЕДИНЕНИЕ, что делает запрос более громоздким, но полностью контролируемым.

Пример идеального запроса (максимальная оптимизация):


ВЫБРАТЬ
    Адреса.Страна КАК Страна
ИЗ
    Документ.ЗаказКлиента КАК Заказ
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагент
        ПО Заказ.Контрагент = Контрагент.Ссылка
        
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК ГоловнойКонтрагент
        ПО Контрагент.ГоловнойКонтрагент = ГоловнойКонтрагент.Ссылка
        
        // Предполагаем, что адрес хранится в контактной информации или реквизите
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Адреса КАК Адреса
        ПО ГоловнойКонтрагент.ЮридическийАдрес = Адреса.Ссылка
ГДЕ
    Заказ.Ссылка = &ЗаказКлиента

Архитектурный рефакторинг: Нормализация данных

В ходе анализа мы затронули важный вопрос: нужно ли вообще вычислять страну «на лету» через такую длинную цепочку?

Денормализация в 1С: Архитектура 1С часто предполагает осознанное нарушение нормальных форм БД. Если страна происхождения важна для бизнес-логики заказа (например, от нее зависит маршрут логистики или налогообложение), то правильно хранить значение страны непосредственно в документе ЗаказКлиента.

Почему это правильно:

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

Рефакторинг алгоритмов: Читаемость и поддержка

Еще одной темой стало написание сложных алгоритмов, таких как склонение ФИО. Часто встречается код, написанный «в потоке», который трудно читать. Рассмотрим, как можно улучшить структуру кода.

Пример подхода к рефакторингу сложной функции (на примере рекурсивной обработки строк):


// Плохой пример (условно):
// Функция Падежик(z1,z2...) - непонятные названия переменных, 
// отсутствие комментариев, сложная вложенность.

При рефакторинге такого кода мы должны:

  1. Дать переменным осмысленные имена (вместо z1 — ИсходнаяСтрока, вместо z2 — Падеж).
  2. Разбить сложную функцию на несколько простых, каждая из которых делает одно действие (Single Responsibility Principle).
  3. Убрать «магические числа» и заменить их на именованные константы или перечисления.

Пример того, как может выглядеть структурированный подход к задаче склонения (упрощенно):


Функция СклонятьПолноеИмя(Знач ПолноеИмя, Знач Падеж) Экспорт
    
    МассивСлов = СтрРазделить(ПолноеИмя, " ");
    Результат = "";
    
    Для Каждого Слово Из МассивСлов Цикл
        // Выделяем логику склонения одного слова в отдельную функцию
        СклоненноеСлово = СклонитьОдноСлово(Слово, Падеж); 
        Результат = Результат + СклоненноеСлово + " ";
    КонецЦикла;
    
    Возврат СокрЛП(Результат);
    
КонецФункции

Пример рефакторинга UI-кода (Управляемые формы)

Рефакторинг касается не только серверной логики, но и кода форм. Часто код выбора периода дублируется в разных командах. Рассмотрим пример использования паттерна передачи контекста через ДополнительныеПараметры.

Вместо того чтобы писать отдельный обработчик для каждой пары дат (период гарантии, период ответов), мы создаем универсальный механизм:


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

&НаКлиенте
Процедура ПериодВыборЗавершение(Результат, ДополнительныеПараметры) Экспорт
    
    Если Результат = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Используем переданный контекст для заполнения нужных полей
    Объект[ДополнительныеПараметры.ДатаС] = Результат.ДатаНачала;
    Объект[ДополнительныеПараметры.ДатаПо] = Результат.ДатаОкончания;
    
КонецПроцедуры

Преимущества такого подхода:

Выводы

Анализируя обсуждение, можно сформулировать несколько правил качественного программирования в 1С:

  1. Избегайте точек: Не используйте длинные цепочки разыменования (А.Б.В.Г) ни в коде, ни в запросах. Это скрывает проблемы производительности и обработки NULL.
  2. Думайте о данных: Иногда лучше добавить реквизит в документ (денормализация), чем каждый раз вычислять значение через сложные связи.
  3. Разделяйте ответственность: Функции должны быть небольшими и понятными. Сложные алгоритмы лучше разбивать на части.
  4. Унифицируйте интерфейс: Используйте универсальные процедуры для повторяющихся действий в формах.

Рефакторинг — это не разовая акция, а постоянная часть процесса разработки. Писать сразу «чисто» получается редко, поэтому возвращаться к коду для его улучшения — это признак профессионализма, а не ошибка.

← На главную