1с рефакторинг что это
Заметки из Зазеркалья
Реализовано в версии 8.3.5.1068.
Мы реализовали в конфигураторе некоторые функции рефакторинга, а также функции автоматического преобразования модальных методов и участков кода. Необходимость этих инструментов объясняется довольно просто.
Код прикладных решений должен быть понятным. Тогда их легко поддерживать и модифицировать. Но в процессе разработки не всегда можно уследить за этим. Особенно если в модификации прикладного решения участвует большая команда разработчиков. Поэтому «хорошим тоном» считается периодическое «наведение порядка» в имеющемся коде, с целью облегчения его понимания. Этот процесс называют рефакторингом. Он не меняет внешнее поведение прикладного решения.
Рефакторинг, безусловно, полезен сам по себе, но иногда он бывает необходим. Например, перед изменением существующей функциональности или перед реализацией новых функций. То есть сначала нужно сделать имеющийся код понятным, а уже затем его модифицировать.
Задача перевода существующего прикладного решения в режим работы без использования модальности как раз является одной из задач такого класса. Поэтому в конфигураторе мы объединили в одном меню функции рефакторинга и инструменты, позволяющие преобразовывать модальные фрагменты кода:
Функции рефакторинга
Функции рефакторинга позволяют сделать код более понятным и удобным для восприятия. Например, вы можете оформить часть кода в виде отдельной процедуры или функции (команда Выделить фрагмент):
Вы можете сформировать заготовку для описания назначения и параметров процедуры или функции (команда Создать описание процедуры):
А ещё вы можете изменить имя локальной или экспортной переменной, процедуры, функции во всех местах её фактического использования (команда Переименовать):
Кроме этого вы можете просто найти все места, где используется некоторая переменная, функция или процедура. С помощью команды Найти использование:
Отказ от модальности
В общем виде задача отказа от модальности заключается в том, чтобы заменить модальные методы их асинхронными аналогами. Мы уже рассказывали об этом в начале года.
Если вы преобразуете модальную функцию, то платформа добавит инициализацию переменной, в которую возвращается значение функции. Потому что в общем случае эта переменная может использоваться далее в той же процедуре:
Другой случай. Например, фрагмент, который вы преобразуете, содержит две ветки исполнения, одна из которых включает в себя модальный код, а другая нет. Платформа преобразует такой фрагмент в две процедуры:
Однако если вы преобразуете код, который находится, например, в модуле управляемого приложения, то полностью автоматическое преобразование вам выполнить не удастся:
Дело в том, что у глобального контекста нет свойства, позволяющего сослаться на него самого (ЭтотОбъект). А значит процедуру, обрабатывающую оповещение, нельзя разместить тут же, в модуле управляемого приложения. Её можно разместить, например, в каком-нибудь общем модуле. Эти действия вам нужно будет выполнить вручную:
В более сложных случаях автоматическое преобразование также возможно с помощью одного или нескольких инструментов.
Например, модальный вызов располагается внутри вызываемой процедуры:
В данном случае последовательность этих двух операций показана лишь для наглядности. На самом деле в этом примере платформа сразу же преобразовала бы Предупреждение() в конечный вариант, известив вас «в процессе» о том, что процедура так же будет преобразована к асинхронному виду:
Однако в сложных фрагментах кода, при большой вложенности, такое автоматическое преобразование может не работать. Тогда мы рекомендуем вручную выделять в отдельные процедуры те фрагменты, которые содержат модальность. А затем уже преобразовывать модальные процедуры и модальные методы.
Например, если в исходной процедуре ниже вы попытаетесь преобразовать Предупреждение(), у вас ничего не получится. Так как вложенность условий довольно глубока. Поэтому сначала нужно выделить в отдельную процедуру законченный фрагмент, содержащий модальный вызов. В данном случае вы можете взять целиком внутреннее условие.
А после этого можете уже автоматически преобразовать Предупреждение(). И, естественно, платформа известит вас о том, что процедуру она также преобразует к асинхронному виду.
Кроме перечисленных команд, позволяющих выполнять «одиночные» преобразования, мы сделали несколько инструментов для автоматической обработки модулей целиком и даже для обработки всей конфигурации. Эти инструменты по своим возможностям не идентичны тому, что умеют «одиночные» преобразования. Они обеспечивают работу в менее сложных случаях.
Например, с помощью команды Найти модальные вызовы модуля вы можете получить список всех строк и операторов модуля, которые требуют преобразования:
Вы можете даже автоматически Преобразовать модальные вызовы модуля. В этом случае платформа выполнит преобразования, для которых не требуется ваше вмешательство, и выведет итоговую информацию. А в окне результатов поиска вы увидите строки, которые платформа не смогла преобразовать автоматически и причину неудачи.
В этом случае перед началом вы можете выбрать только анализ или сразу же и преобразование, а также можете задать некоторые параметры этих действий.
По окончании анализа, например, вы увидите сводную информацию, а также более подробную по каждому из модальных вызовов, имеющихся в конфигурации:
Рефакторинг в редакторе модулей
Краткое содержание
Пользуйтесь Ctrl+Alt+R в редакторе модулей.
Мотив
Неожиданно выяснилось, что многие знакомые программисты не пользуются подменю Рефакторинг в редакторе модулей конфигуратора. Как ни странно,
— Описания подменю Рефакторинг нет в справке конфигуратора (платформа 8.3.16.1224);
— Описания подменю Рефакторинг нет на Инфостарте;
— В справке конфигуратора сочетание клавиш Ctrl+Alt+R не упоминается.
В результате решил написать об этом статью.
Подменю Рефакторинг
Команда Переименовать
Рис 1. Команда Переименовать
Действие команды зависит от текущего положения курсора в тексте модуля.
Рассмотрим варианты:
Команда Выделить фрагмент
Из выделенного фрагмента кода создается новая процедура или функция с постфиксом Фрагмент. А вместо выделенного фрагмента вставляется вызов новой процедуры или функции.
Удобно использовать, когда надо разбить слишком длинную процедуру на несколько частей. Команда все сделает аккуратно, передаст параметры, локальные переменные будут описаны в перечислены в операторе Перем.
Команда Создать описание метода
Создает комментарии перед описанием процедуры или функции. Комментарии создаются в стандартном формате, который используется для формирования контекстной подсказки.
Команда Создать обработку оповещения
Помогает создавать обработки оповещения для асинхронных методов типа ПоказатьВопрос().
Пример:
Подменю нерекомендуемые синхронные вызовы
Начиная с платформы 8.3.3 фирма 1С придерживается политики отказа от модальных вызовов. Команды подменю предназначена для перехода на асинхронные вызовы.
Рефакторинг в 1С
Рефакторинг — это процесс изменения кода на встроенном языке с целью сделать код чище, повысить читаемость и улучшить структуру кода не изменяя при этом изначального смысла и внешнего поведения этого кода. Конфигуратор предоставляет несколько инструментов, которые могут помочь при проведении рефакторинга кода в прикладном решении.
Эти инструменты можно найти в подменю «Рефакторинг» контекстного меню редактора кода или в главном меню конфигурации («Текст»-«Рефакторинг»).
Подробнее о каждом из этих инструментов и будет рассказано ниже.
Общие средства рефакторинга
Начнем с общих средств рефакторинга кода, а перед этим напомню, что подробнее о синхронных и асинхронных вызовах можно прочитать в этой статье, а о модальности и блокирующих окнах в этой.
Выделить фрагмент
Эта команда преобразует выделенный фрагмент кода в процедуру или функцию. Фрагмент должен быть синтаксически целостным. Код нового метода располагается сразу после родительского метода.
Если переменные из выделяемого фрагмента используются в оставшейся части родительского метода, то эти переменные передаются новому методу в виде параметров.
Если родительский фрагмент имеет какую-либо директиву компиляции, то новый метод будет иметь точно такую же директиву.
Переименовать
Эта команда предназначена для изменения имен переменных и методов. При переименовании методов-обработчиков в модуле формы выполняется автоматическая замена соответствующих значений свойств элементов формы и команд на правильные.
Для экспортных переменных и модулей выполняется проверка уникальности в рамках конфигурации и отсутствия синонимов среди методов и свойств объектов платформы. Если условие соблюдено, пользователю предлагается глобальная замена идентификатора.
Создать описание метода
Простая команда, которая создает заготовку описания метода. Это описание будет использоваться для контекстной подсказки при использование метода в тексте модулей.
Создать обработку оповещения
Эта команда облегчает применение асинхронных методов. Для того, чтобы команда стала доступна нужно ввести имя метода и открывающую скобку, курсор нужно расположить на имени метода.
Рефакторинг синхронных вызовов
Теперь рассмотрим команды, которые помогают при переходе на асинхронные вызовы
Преобразовать вызов
Эта команда заменяет простые случаи использования синхронных методов на их асинхронные аналоги. Случай не считается простым если, например, использован внутри цикла, внутри перехвата исключения, внутри процедуры, вызываемой из другой процедуры и тд. Для подобных случаев пользователю предлагается выполнить частичное преобразование.
Преобразовать в асинхронную процедуру
Команда преобразует метод к виду, подходящему для использования в асинхронных вызовах. Эту команду следует использовать в тех случаях, когда необходимо преобразовать модальные вызовы внутри процедуры/функции, которая вызывается из других процедур/функций. После использования этой команды нужно превратить модальные вызовы в асинхронные аналоги при помощи команды преобразования модального вызова.
Выделить в асинхронную процедуру
Эта команда превращает выделенный фрагмент кода в отдельную процедуру или функцию, при этом выделенный метод преобразуется к асинхронному виду.
Найти вызовы модуля
Выводит список используемых синхронных методов в текущем модуле.
Преобразовать вызовы модуля
Команда преобразует все простые случаи использования синхронных вызовов в текущем модуле на их асинхронные аналоги.
На этом все, надеюсь, что эта статья была Вам полезна.
Если Вы нашли ошибку или неточность, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.
Рефакторинг на примере среды программирования 1С
Рефакторинг – переработка старого кода или написание нового кода так, чтобы он был более приспособлен к сопровождению и изменениям
Основные причины роста сложности (трудоемкости) модификации программ по мере добавления в них новой функциональности «традиционным» методом:
Рефакторинг – подход к написанию кода, который ведет к устранению или снижению влияния нижеуказанных проблем. В основе большинства положений рефакторинга лежат два ведущих принципа
Код должен быть структурирован так, чтобы:
Мартин Фаулер: «Написать код, понятный компьютеру может каждый. Хороший программист пишет код понятный людям».
Положения рефакторинга:
1. Тексты функций и процедур не должны быть очень длинными – 10-15 строк максимум. Исключение может составлять текст функции с комплексным длинным запросом. В этом случае рекомендуется выделять отдельную функцию, отвечающую за формирование текста запроса или за формирование результата запроса. Это возможно одно из самых главных правил написания сопровождаемого кода.
2. Полезно выделять большие циклы (когда внутри цикла много команд) в отдельные процедуры, название которых разъясняет принцип того, что делается в цикле (по аналогии с заменой комментария).
3. Текст должен быть самодокументирующимся – если вам необходимо вставить в текст кода комментарий, подумайте, возможно, вместо комментария стоит выделить функцию с названием, которое бы смогло заменить необходимость вставки комментария.
4. Имена функций и процедур, переменных должны быть написаны языком бизнес-логики, а не техническим языком (звучащие имена методов). Они не должны быть обезличены. Например, вместо функции ПолучитьТекстЗапроса лучше написать ПолучитьТекстЗапросаДляРасчетаУровняЗапасов.
5. Общие модули не должны быть глобальными. Должна происходить изоляция логики. Название функций и процедур не должны дублировать названий общих модулей. Например, некорректно написать ПолныеПрава.ЗаписатьСправочникСПолнымиПравами(). Лучше в этом случае функцию назвать просто ЗаписатьСправочник(): ПолныеПрава.ЗаписатьСправочник().
6. Общие модули выступают как библиотеки функций, функциональность в них должна соответствовать теме, выраженной в названии общего модуля
7. Аналогом объектов в объектно-ориентированных языках в 1С являются обработки, которые сохраняют свойство инкапсуляции и имеют время жизни (в отличие от общих модулей). При создании новой логически завершенной функциональности, которая имеет объектную сущность, но не хранится в базе данных (процедуры расчета (уровней запаса и т.д.), контроля, расширенные таблицы значений и т.д.), необходимо использовать обработки 1С, инкапсулируя свойства объектов и применяя экспортные процедуры для расчета. Такие обработки могут не иметь формы. В других случаях форма может быть использована, как средство тестирования процедуры расчета. Использование обработок имеет дополнительное преимущество – они могут быть перенесены в другие схожие конфигурации.
8. Много экспортных методов в одном общем модуле или в одной обработке – это плохо. Очевидно, что общий модуль или обработка пытаются охватить слишком широкую тематику. В этом случае лучше произвести смысловую разбивку и иметь два модуля или две обработки (или даже еще больше).
9. Каждый объект должен работать только со своими значениями (модифицировать) – принцип инкапсуляции. Если в объекте есть функции, которые изменяют данные других объектов, лучше поместить подобные функции в модули изменяемых объектов или выделить их в отдельные обработки или общие модули (в зависимости от сущности указанных изменений). Например, в форме справочника Контрагентов происходит редактирование Контактных данных. При записи справочника Контрагентов они сохраняются. Функция перезаписи контактных данных не должна находиться в модуле справочника Контрагенты (и тем более не в форме справочника). Она должна только оттуда вызываться. Где она будет находиться зависит от логики решения (в отдельной обработке/общем модуле или в модуле регистра сведений контактные данные).
10. Процедуры получения данных и обработки данных должны находиться в модулях объектов. В форме объектов должны быть только интерфейсные процедуры – взаимодействие с пользователем, изменение элементов интерфейса – без получения данных и их обработки.
11. Расширение предыдущего правила – при написании отчета, функция, которая получает табличный документ (выход отчета) должна находиться в модуле отчета – так, чтобы всегда можно было вызвать эту функцию не только из самого отчета.
12. Для косвенной передачи данных из формы в модуль использовать ДополнительныеСвойства формы – не заводить никаких внешних глобальных переменных.
13. В случае если необходимо получить выборку простого списка данных (из одного, в крайнем случае – двух объектов данных) и логика подобной выборки включает более 1-2 строк, необходимо выделять такие выборки в отдельные функции. Указанные функции помещать в модули менеджеров объектов. Пример: Справочники.Контрагенты.ПолучитьПоставщиковМенеджера(Менеджер) – функция выбирает из справочника контрагентов поставщиков, отфильтрованных по менеджеру.
14. Принцип сокрытия полей (инкапсуляция): Не использовать прямое обращение к полям объекта, при вызове его из другого объекта.
Вместо
ОтчетОтгрузки.Контрагент = А;
ОтчетОтгрузки.Склад = Б;
ТД = ОтчетОтгрузки.ПолучитьТабличныйДокумент();
15. Необходимо избавляться от временных переменных. Использование временных переменных усложняется тем, что необходимо контролировать весь текст функции, чтобы понять, где происходит их модификация. Вместо использования временных переменных стоит напрямую обращаться к функции, которая возвращает значение, помещаемое во временную переменную, в том месте, где происходит использование временной переменной. Пример ненужных временных переменных:
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать(); // больше результат нигде не используется
Лучше использовать:
Выборка = Запрос.Выполнить().Выбрать();
17. Внутри функции не запоминать результат во временной переменной, а сразу же возвращать в точке, где он вычисляется. Если логика более сложна, требует модификации результата в теле функции, стоит подумать над возможностью упрощения текста функции, чтобы не использовать временную переменную. В крайнем случае, имя переменной должно быть Результат. Это имя необходимо использовать ТОЛЬКО для возвращаемого значения функции, для других целей использовать модификации имени (например, РезультатЗапроса).
18. Не использовать в запросах вложенные запросы – с одной стороны это нарушает читабельность запроса и его сопровождаемость, с другой стороны – ухудшает производительность выборки. Вместо вложенных запросов нужно использовать временные таблицы с пакетом запросов.
19. Дублирование кода – это плохо. Следует избегать дублирования кода даже в рамках одной функции (например, 2 одинаковых цикла подряд с небольшими отличиями друг от друга). Выделять повторяющийся код в отдельную небольшую функцию.
20. Длинные списки параметров, передаваемых процедуре или функции (от 5-6 и более) заменять на Структуру значений. Но Аккуратно! Баланс нужно чувствовать. Можно сказать только одно – подобное чувство приходит с опытом.
21. Вместо наборов строк, как идентификаторов выбора, необходимо использовать перечисления, добавляемые в структуру метаданных.
Т.е. вместо
Если ВариантВызова = «КонтрольЗаказов» Тогда
.
ИначеЕсли ВариантВызова = «КонтрольОтгрузок» Тогда
.
КонецЕсли;
Нужно завести перечисление Перечисление.ВариантыКонтроляЗадолженности с вышеуказанными значениями.
Подобный подход позволит еще на этапе программирования (а не на этапе работы пользователя) выявлять ошибки, связанные с возможными опечатками во время программирования. Дополнительно у программиста появляется четкое представление о конечности возможных вариантов.
22. Необходимо избегать цепочек вызова типа А.Получить().Получить().Выполнить().Разобрать(). Если объект А – не типовой объект, а объект, который может быть модифицирован (документ, обработка, общий модуль), то необходимо в модуль объекта А добавить новую функцию «ВыполнитьРазбор()», в которой прописать требуемую логику, а вызывать уже А.ВыполнитьРазбор(). Исключением из этого правила являются те случаи, когда мы не можем модифицировать объект. Например: Запрос.Выполнить().Выбрать(). Такая запись в этом случае предпочтительнее, чем использование временной переменной
23. Не использовать многовариантных условных операторов – несколько выражений Иначе в поле выбора. Для того чтобы избавиться от нескольких условий, можно использовать следующие подходы:
Последний возврат должен содержать результат основной логики. Остальные – условную логику функции.
24. Вложенность условий необходимо по возможности заменять на одно условие (И/ИЛИ/НЕ). Зачастую в подобной замене помогает инверсия условных выражений (иногда потребуется выделение функций).
Например в схеме:
Если Условие1 Тогда
Оператор1;
Если Условие2 Тогда
Оператор2;
КонецЕсли;
Иначе
Оператор3;
КонецЕсли;
Обработку можно заменить следующим образом:
Если Не Условие1 Тогда
Оператор 3;
Возврат
КонецЕсли;
Если Условие2 Тогда
Оператор 2
КонецЕсли;
25. Сложные условия, в составе которых идет несколько проверок для повышения читаемости следует выделять в отдельные функции, возвращающие булево значение. Название функции в этом случае позволит более правильно определить суть проверяемого условия и повысить читабельность. Как правило дополнительный плюс этого подхода – то, что подобную функцию получается возможно использовать и в других процедурах.
27. Вместо блока Если Тогда в результате функции, возвращающей булево значение, использовать условное выражение. Например: Возврат (А=5);
28. Не использовать «магические числа» – цифровые значения в конкретных проверках. Для замены магических чисел следует использовать функции, возвращающие число (предпочтительно общих модулей) или, в крайнем случае, – глобальные переменные.
Например, если в рамках задачи идет проверка уровня задолженности 50%, то не стоит использовать прямое указание цифры:
Если ПроцентЗадолженности = 50 Тогда.
Заменить на:
Если ПроцентЗадолженности = РазрешенныйУровеньОплатыПоДоговоруСДистрибьютором() Тогда
А саму функцию описать как
Функция РазрешенныйУровеньОплатыПоДоговоруСДистрибьютором() Экспорт
В будущем это значение всегда можно сделать параметризируемым и изменений вносить придется значительно меньше.
Если «магическое число» используется в запросе, то лучше передавать его как параметр запроса, а в момент передачи параметра вызывать соответствующую функцию.
29. Неявные моменты, необрабатываемые функцией, не заменять на возврат неопределенного значения, возврат текстового описания ошибки, или возврат кода ошибки. Необходимо использовать вызов исключения, с текстом, описывающим суть проблемы, со ссылкой на место проблемы. Такой подход позволит на этапе тестирования лучше выявлять проблемы, а на этапе сопровождения ошибка будет возникать именно в том месте, где есть проблема, а не в вышестоящих операторах, пытающихся обработать неопределенное значение. Дополнительно текст исключения позволит лучше понять, что происходит. Например, в функции ОбработатьЗаполнение, если в качестве основания заполнения передан неверный тип документа, можно вызывать исключение, сообщая что данный тип не поддерживается при вводе на основании.
30. Постараться не использовать функции, которые одновременно возвращают значение и в теле функции изменяют что-то. Для изменения данных лучше использовать процедуры. Функции должны либо проверять, либо возвращать рассчитанные значения. Если нужно в теле функции вернуть несколько значений, стоит использовать СтруктуруЗначений для возврата.
Что такое рефакторинг
Как сделать код чище и понятнее.
В среде разработчиков можно услышать: «Мне нужен год на рефакторинг» (полгода, месяц, неделя, хотя бы денёк). Что это значит и зачем это нужно — разберём.
На примере кафе
Представим такую ситуацию: мы открыли кафе, построили там классную кухню и наняли шеф-повара. Когда кафе только запускалось, в меню были самые простые блюда, которые можно было разогреть в микроволновке. Вы купили микроволновку и поставили на кухне. Рядом разместили стеллаж для всего нужного.
Через два месяца дела пошли лучше, и мы решили добавить в меню выпечку. Купили духовой шкаф, протянули провода, добавили рядом стойку с подносами. Место на кухне уменьшилось, повар всё время перешагивает через провода и обходит стойку, но работать можно.
Ещё через месяц поставили фритюрницу и миксер для теста. К ним тоже протянули провода, добавили рядом шкафы для утвари и наняли второго повара. В итоге на кухне полный бардак, всё друг другу мешает, а блюда готовить неудобно. Если мы захотим добавить новую плиту, то это будет уже сложно: места нет, хотя по площади можно поставить хоть две таких плиты.
В этот момент приходит кухонный проектировщик и рисует всё заново: где что должно стоять.
Через неделю на кухне всё по-другому: оборудование стоит так, чтобы не мешать поварам, провода спрятаны в короба, а стойки с оборудованием не загораживают выход. При этом меню в кафе не изменилось: посетители даже не знают, что у нас что-то происходило на кухне, потому что мы оптимизировали процессы, а не меню. Это и есть рефакторинг — когда мы меняем что-то внутри так, что снаружи это незаметно, но работать дальше становится проще.
Когда нужен рефакторинг в программировании
Есть два подхода к рефакторингу: плановый и по необходимости.
Плановый рефакторинг означает, что разработчики сразу закладывают время на рефакторинг в цикл разработки. Например, каждые четыре спринта или каждые полгода.
В больших компаниях, где много легаси-кода, могут быть вообще отдельные команды, которые занимаются только рефакторингом старья. Это помогает остальным командам быстрее понимать, что в нём происходит и как им пользоваться.
Второй подход — рефакторинг по необходимости, когда добавление новых возможностей тормозится из-за того, что их сложно интегрировать в старый код. Тогда мы говорим «Стоп машина» и берём какое-то время на реорганизацию всего, что было.
На что смотрят при рефакторинге кода
Главный показатель успешного рефакторинга — после него код стал чище, проще и понятнее.
Например, если переменная Z в программе отвечает за количество покупателей, то лучше её заменить на customerCount — так будет проще разобраться в коде и понять, что там происходит.
Если фрагмент кода повторяется больше одного раза, то его чаще всего выносят в отдельную функцию или метод. В этом случае будет легче заменить код в одном месте, чем искать повторяющиеся фрагменты по всей программе.
Ещё программисты обращают внимание на размер функций, методов и классов. Если функция получается слишком большой, чтобы поместиться на одном экране, — её разбивают на две, чтобы упростить читаемость кода.
Иногда, чтобы сделать код проще, разработчики выносят часть функций в отдельный файл и подключают его к основной программе.
А можно без рефакторинга?
Жить можно и без рефакторинга, но чем дальше без него — тем тяжелее работать. Рефакторинг — это как наведение порядка на рабочем месте. Если долго им не заниматься, со временем работать становится неудобно. Регулярный рефакторинг помогает не замедлять дальнейшую разработку в больших командах.
Без рефакторинга можно только в маленьких продуктах, которые развиваются медленно.
Что дальше
В следующей статье попробуем применить эти знания на практике и отрефакторить какой-нибудь из наших старых проектов, чтобы посмотреть, как изменится код и что получится в итоге.