Здравствуйте, в этой статье мы постараемся ответить на вопрос: «Что такое рефакторинг и в чем его цели». Если у Вас нет времени на чтение или статья не полностью решает Вашу проблему, можете получить онлайн консультацию квалифицированного юриста в форме ниже.
Есть гласное правило для всех программистов – код должен быть лаконичным, хорошо структурированным и понятным для разработчиков, работающих с ним. Проблема в том, что написать такой код с первого раза – очень сложная задача. Каким бы опытным ни был программист, начальство заставит его спешить, заказчики будут менять требования по ходу разработки, а иногда код будет становиться непонятным из-за банального недосыпа. Более того, сами языки программирования регулярно совершенствуются и обретают новые возможности, позволяя заметно сократить количество кода. Поэтому и нужен рефакторинг.
В каких случаях нужен рефакторинг?
Есть ряд ситуаций, которые «кричат» о необходимости рефакторинга:
-
Попытка внести любое улучшение или добавление новой функции в приложение превращается в проблему для разработчиков, а сроки выполнения, на первый взгляд, несложных операций затягиваются на неадекватный срок из-за того, что база кода напоминает дебри.
-
Сроки добавления новых функций в приложение на постоянной основе становятся размытыми, потому что разработчикам приходится закладывать время на анализ кода.
-
Приходится выполнять идентичные процедуры в разных участках кода (объектах, классах) вместо того, чтобы внести изменение в одном классе, и оно возымело бы эффект в других участках ПО.
-
Код не соответствует общепризнанным в компании практикам оформления, из-за чего не может использоваться для дальнейшей разработки с учетом ранее установленных требований.
Зачем нужен рефакторинг
Когда программисты пишут код, они решают определенную задачу. Иногда происходит так, что решить ее нужно в очень сжатые сроки. От спешки страдает не только программист, но и код: он становится сложным, хаотичным и неструктурированным.
Неструктурированный код можно сравнить с неструктурированным текстом — без заголовков и абзацев. Чтобы структурировать такой код и сделать его понятнее, разработчики используют рефакторинг.
Американский программист и автор множества книг по разработке ПО Мартин Фаулер определяет рефакторинг как «изменение внутренней структуры ПО без изменения его наблюдаемого поведения, призванное облегчить его понимание и удешевить модификацию». Разработчики переписывают какие-то части кода, чтобы они стали понятнее, лаконичнее, а еще — чтобы было проще добавлять новую функциональность.
Как встроить рефакторинг в свой рабочий процесс?
Обычно самый первый вопрос — как правильно выделить время под рефакторинг. Заводить ли для этого отдельные таски и когда рефакторить, если фичи в приоритете?
Ответ такой: рефакторинг должен проводиться непрерывно на всем этапе разработки.
Обычно это выглядит так:
— Вы взяли задачу и начали читать код. На этом моменте обычно вам уже придется что-то подрефакторить.
— Потом вы приступили к разработке. Буквально через 5-10 минут, максимум полчаса-час, у вас должен быть рабочий кусок кода, покрытый тестом. Тут же надо его рефакторить.
— Снова разработка, снова покрытый тестами кусок кода через непродолжительный отрезок времени.
И такими вот циклами и движетесь вперед до решения задачи. Как только вы наткнулись на кусок кода, в который ваша фича не лезет никак — останавливаетесь в прогрессе и начинаете рефакторить.
Если вы делаете это в парадигме TDD, то этот подход будет называться Red-Green-Refactor. То есть сначала пишете красный тест на какой-то фрагмент функционала, потом пишете код, который делает ваш тест зеленым, потом рефакторите.
Целесообразность применения рефакторинга
Рефакторинг бывает двух вариантов: запланированный либо проводящийся по мере надобности.
Плановый – это тот, для которого в цикле разработки изначально заложено необходимое время. К примеру, он будет выполняться раз в полгода, или после каждых четырех спринтов.
Крупные компании часто создают специальные группы, работа которых состоит исключительно в рефакторинге старых кодов (легаси-кода в больших фирмах обычно много). После этого они становятся более понятными и простыми в использовании для других сотрудников.
Второй вариант – рефакторинг по мере надобности. Это когда необходимо внести в код новые возможности, но они плохо интегрируются со старыми. Тогда приходится их пересматривать и реорганизовывать.
Представим такую ситуацию: мы открыли кафе, построили там классную кухню и наняли шеф-повара. Когда кафе только запускалось, в меню были самые простые блюда, которые можно было разогреть в микроволновке. Вы купили микроволновку и поставили на кухне. Рядом разместили стеллаж для всего нужного.
Через два месяца дела пошли лучше, и мы решили добавить в меню выпечку. Купили духовой шкаф, протянули провода, добавили рядом стойку с подносами. Место на кухне уменьшилось, повар всё время перешагивает через провода и обходит стойку, но работать можно.
Ещё через месяц поставили фритюрницу и миксер для теста. К ним тоже протянули провода, добавили рядом шкафы для утвари и наняли второго повара. В итоге на кухне полный бардак, всё друг другу мешает, а блюда готовить неудобно. Если мы захотим добавить новую плиту, то это будет уже сложно: места нет, хотя по площади можно поставить хоть две таких плиты.
В этот момент приходит кухонный проектировщик и рисует всё заново: где что должно стоять.
Через неделю на кухне всё по-другому: оборудование стоит так, чтобы не мешать поварам, провода спрятаны в короба, а стойки с оборудованием не загораживают выход. При этом меню в кафе не изменилось: посетители даже не знают, что у нас что-то происходило на кухне, потому что мы оптимизировали процессы, а не меню. Это и есть рефакторинг — когда мы меняем что-то внутри так, что снаружи это незаметно, но работать дальше становится проще.
- Обнаружив, что в программу необходимо добавить новую функциональность, но код программы не структурирован удобным для добавления этой функциональности образом, сначала произведите рефакторинг программы, чтобы упростить внесение необходимых изменений, а только потом добавьте функцию.
- Перед началом рефакторинга убедитесь, что располагаете надежным комплектом тестов. Эти тесты должны быть самопроверяющимися.
- При применении рефакторинга программа модифицируется небольшими шагами. Ошибку нетрудно обнаружить.
- Написать код, понятный компьютеру, может каждый, но только хорошие программисты пишут код, понятный людям.
Самый важный урок, который должен преподать данный пример, это ритм рефакторинга: тестирование, малые изменения, тестирование, малые изменения, тестирование, малые изменения. Именно такой ритм делает рефакторинг быстрым и надежным.
При проведении рефакторинга важным предварительным условием является наличие надежных тестов.
Правила разработки тестов
- Делайте все тесты полностью автоматическими, так чтобы они проверяли собственные результаты.
- Комплект тестов служит мощным детектором ошибок, резко сокращающим время их поиска.
- Чаще запускайте тесты. Запускайте тесты при каждой компиляции — каждый тест хотя бы раз в день.
- Получив сообщение об ошибке, начните с создания теста модуля, показывающего эту ошибку.
- Лучше написать и выполнить неполные тесты, чем не выполнить полные тесты.
- Подумайте о граничных условиях, которые могут быть неправильно обработаны, и сосредоточьте на них свои тесты.
- Не забывайте проверять, чтобы в случае возникновения проблем генерировались исключительные ситуации.
- Опасение по поводу того, что тестирование не выявит все ошибки, не должно помешать написанию тестов, которые выявят большинство ошибок.
Декомпозиция рефакторинга
Как и для пользовательских историй, разбиение на подзадачи важно и для рефакторинга, так как это помогает поддерживать постоянный поток процесса разработки. В Таблице 1 представлены некоторые полезные методы для декомпозиции задач по рефакторингу и пример по каждому из них.
1. По пользовательским сценариям или пользовательским историям — рефакторить инкрементально историю за историей, или сценарий за сценарием, или каким-то иным способом выделить несколько функциональных областей для их последовательного рефакторинга. | |
Оптимизировать запросы к БД и внедрить кеширование данных, чтобы система работала быстрее | …Отрефакторить все функции управления пользователями… Далее функционал просмотра каталога… Далее функционал проверки работоспобности |
2. По компонентам — сначала провести рефакторинг кода одного компонента, а потом перейти к следующему. | |
Поменять процесс индексирования на пакетную обработку, чтобы процесс был как минимум в 2–3 раза быстрее для средней веб-страницы | …Рефакторим парсинг (компонент парсера)… Далее экстрактор сущностей (компонент-анализатор)… Далее хранение в индексе (компонент индекса) |
3. По интерфейсам/реализациям — сначала создаем интерфейс и оборачиваем им старую функциональность, а потом рефакторим саму функциональность. | |
Извлечь все параметры синтаксического анализа в xml-файл конфигурации, чтобы процесс можно было легко настроить без изменения кода | …Ставим PINGID Protocol server и тестим его с помощью провайдера идентификации mock…Читаем конфигурацию из файла в любом формате… Рефакторим функциональность конфигурирования, чтобы поддерживать в должном состоянии валидацию структуры и схемы |
4. Уменьшение устаревшего кода / Разделение монолита — постепенно переносим функциональность из legacy-компонента (компонент с устаревшим кодом) в новые; как только все будет перемещено, удаляем старый код. | |
Заменить базу данных пользовательским поисковым индексом, чтобы производительность индексирования и поиска повысилась в 10-20 раз | …Сначала переносим индексацию данных в поисковый индекс… Далее переносим словари |
5. Точечный рефакторинг / Инкапсуляция результата – сначала рефакторим там, где это нужно, но потом извлекаем измененную сущность и инкапсулируем ее в компонент, класс или метод/функцию. | |
Заменить ad hoc синтаксический анализ на анализ на основе грамматики так, чтобы изменение правил синтаксического анализа стало простым и без необходимости кодирования | …Рефакторим текущий код так, чтобы он использовал нотацию грамматик… Извлекаем эту функциональность и помещаем ее в движок грамматик |
Когда нужен рефакторинг в программировании
Существует два вида рефакторинга: плановый и при необходимости.
Рефакторинг, который изначально закладывается программистами в цикл разработки, называется плановым. Например, его могут планировать на каждые 6 месяцев или каждые 4 сплита.
В крупных компаниях, где обычно много legacy-кода, вообще формируются отдельные команды, занимающиеся исключительно рефакторингом старья. Благодаря этому остальные команды легче и быстрее понимают, что происходит в этом коде и как его использовать.
Второй вариант – рефакторинг при необходимости. К нему прибегают, когда возникают сложности с добавлением новых возможностей к старому коду. Тогда мы приостанавливаем процесс и выделяем какое-то время на переустройство того, что было.
Как понять, что проекту нужен рефакторинг
Заказчику стоит задуматься о рефакторинге, если:
- проект работает, но медленнее обычного, часто случаются сбои;
- даже небольшие правки затягиваются по времени;
- разработчики не могут оценить объем задач, поскольку говорят о том, что сначала нужно разобраться;
- при выгрузке изменений в релиз часто встречают баги.
Для разработчика «красные флаги», сигнализирующие о необходимости чистки кода будут такими:
- дублирование кода;
- длинные или ресурсоемкие методы;
- большие классы;
- несгруппированные данные;
- длинные списки параметров;
- избыточные переменные;
- если понимание кода занимает много времени. Иногда код может быть написан с соблюдением принципов DRY и KISS, но работа кода всё-равно остается непонятной разработчикам. Здесь стоит задуматься о смене паттернов проектирования и проектировании другой модели абстракции. Часто помогает и переименование переменных по рекомендациям Мартина Фаулера.
В рамках рефакторинга проводятся, на первый взгляд, элементарные работы:
- перемещение поля из одного класса в другой;
- вынесение фрагмента кода из метода в самостоятельный метод;
- перемещение кода по иерархии классов.
Что такое рефакторинг и каким он бывает?
Рефакторинг – это внутреннее преобразование программ или систем с целью оптимизировать их алгоритмы, улучшить структуру и упростить понимание их работы. При этом внешнее поведение системы или программы не меняется.
Изначально понятие рефакторинга относилось только к коду включало в себя сокращение написанного кода, модификацию методов, избавление от дублирующего кода, удаление неиспользуемых переменных, повышение читаемости и т.д. Однако, в последнее время всё чаще можно встретить определения «системный», «архитектурный» рефакторинг и даже «рефакторинг технической документации».
В целом, разница этих видов – в области применения:
- Рефакторинг на системном уровне подразумевает изменения в логике работы приложения: переход на другие алгоритмы обработки информации и источники данных, изменение структуры данных в базе, изменения механизмов сохранения, получения и передачи данных внутри одной системы;
- Архитектурный рефакторинг охватывает взаимодействие нескольких систем: он может включать в себя изменение механизмов интеграции, замену некоторых системных компонентов, изменение работы сервисов без изменения их контрактов, переход на новую архитектуру.
- Рефакторинг технической документации означает периодическую вычитку технических заданий и спецификаций, улучшение структуры документов (перенос и группировка разделов, работа над логическими переходами), удаление дублирующей информации, работа над формулировками. Цель такая же, как в рефакторинге кода – повышение ясности и читаемости текста. Описание должно быть логичным и последовательным, при прочтении документа не должна возникать необходимость постоянно «перескакивать» в другие разделы за дополнительной информацией.
Определение Мартина Фаулера
Вопрос «Что такое рефакторинг?» часто возникает у программистов-новичков, а иногда и у более опытных разработчиков. Поэтому он регулярно всплывает на форумах в духе StackOverflow.
Там в качестве ответа на вопрос приводят цитату из книги «Refactoring: Improving the Design of Existing Code» за авторством Мартина Фаулера:
Рефакторинг – это контролируемая техника совершенствования структуры существующего кода. Суть рефакторинга заключается во внесении серии мелких изменения (с сохранением функциональности приложения), каждое из которых «слишком мелкое, чтобы тратить на него время». Тем не менее эффект от внесения всех этих изменений достаточно ощутимый.
Также в этой книге рекомендуется выполнять рефакторинг пошагово, чтобы исключить появление ошибок. А пользователи StackOverflow советуют каждое изменение сопровождать применением юнит-тестов. И хотя многие отрицают столь тесную связь этих двух операций, большинство опытных кодеров все же не упускают возможности задействовать тесты на любом из этапов разработки или модификации ПО.
Что не является рефакторингом?
Те, кто не углубляется в изучение терминологии, часто принимает за рефакторинг целую серию других действий, отчасти похожих на рефакторинг. В их числе:
простое переписывание кода,
улучшения функциональной составляющей ПО,
Первое имеет смысл при создании нового ПО или самотренировок.
Второе подразумевает поиск ошибок и их устранение, сам код при этом необязательно должен становиться проще или понятнее для других разработчиков. Цель дебаггинга – заставить программу работать корректно, не наплодив при этом новых ошибок.
Третье может быть связано с модификацией «читаемости» кода, но это необязательная составляющая. Важно сделать ПО лучше с пользовательской точки зрения, а не с точки зрения разработчика.
Четвертый термин чаще всего путают с рефакторингом, потому что они как раз иногда выполняются параллельно, но оптимизация – фокус на производительности программы. Код может стать даже сложнее, но ПО должно работать шустрее.
Но ни что из перечисленного выше не является рефакторингом, и зачастую каждая из процедур выполняется отдельно (за редким исключением, когда некорректное поведение ПО вызвано неправильно написанным кодом).
Вам нужны специальные инструменты для рефакторинга? Мартин Фаулер говорит, что автоматизированные инструменты полезны, но не важны. Он отмечает:
Многие языки имеют IDE, которые автоматизируют стандартный рефакторинг. Это действительно ценная часть моего набора инструментов, позволяющая мне быстрее выполнять рефакторинг. Но такие инструменты не являются необходимыми — я часто работаю с языками программирования без поддержки инструментов, и в этом случае я полагаюсь на небольшие шаги и частое тестирование для обнаружения ошибок.
Многие среды разработки автоматизируют механические аспекты рефакторинга. Инструменты рефакторинга ключевого кода:
- Visual studio intellicode
- Eclipse IDE
- Spring Tool Suite 4
- Rider
- IntelliJ IDEA
- SonarQube
Понятие рефакторинга, причины рефакторинга
Рефакторинг (англ. refactoring) или реорганизация кода — процесс изменения внутренней структуры программы, не затрагивающий её внешнего поведения и имеющий целью облегчить понимание её работы [1]. Мартин Фаулер в [4] приводит несколько иное определение рефакторинга: «Рефакторинг (англ. refactoring) — изменение во внутренней структуре программного обеспечения, имеющее целью облегчить понимание его работы и упростить модификацию, не затрагивая наблюдаемого поведения». Как следует из приведенных определений, сферой применения рефакторинга является исходный код, основной целью — упрощение будущих модификаций и облегчение понимания кода человеком.
Как правило, рефакторинг выполняется путем применения серии стандартизированных «микро-рефакторингов», каждый из которых является (обычно) незначительным изменением в исходном коде компьютерной программы, которое сохраняет поведение программного обеспечения, или, по крайней мере, не изменяет его соответствие функциональным требованиям [1].