Рефакторинг – это процесс изменения внутренней структуры приложения, который не влияет на его функциональность, не устраняет ошибки, но делает код более простым, лаконичным, компактным. Разработчику важно понимать, в каких случаях необходимо делать рефакторинг, каким образом осуществляется эта процедура и в чем вообще заключается ее польза для конкретного проекта.
Зачем нужен рефакторинг и нужен ли
Refactoring можно сравнить с приведением рабочего места в порядок. Стол разработчика с течением времени засоряется бумагами, записками, справочной литературой по языкам программирования и прочими лишними предметами. Если периодически убирать мусор и расставлять предметы по своим местам, суть работы кардинально не изменится, но делать ее будет куда приятнее и проще.
Рефакторинг не ускоряет работу программы, не добавляет и не убирает из нее функционал – для этих задач используется оптимизация. Метод устраняет повторы, слишком «раздутые» участки и другие типичные проблемы, из-за которых код становится сложным для понимания, занимает неоправданно много места, а его доработка занимает гораздо больше времени, чем могла бы.
Можно обойтись и без рефакторинга, но чем дальше идет разработка без него, тем сложнее будет работать с приложением. Особенно ярко это проявляется у крупных продуктов, над созданием которых трудятся большие команды разработчиков.
Простые программы можно не рефакторить с меньшими последствиями, но лучше выработать привычку исправлять код во всех своих проектах.
Когда надо выполнять рефакторинг
Если вопрос о том, зачем нужен рефакторинг кода, как правило, не стоит, то с поиском момента, когда в нем есть необходимость, у разработчиков часто возникают проблемы. Понимание того, при каких обстоятельствах нужно рефакторить структуру приложения, приходит с опытом. Но есть ряд признаков, которые указывают на серьезные проблемы в коде и необходимость его переработки.
Замусориватели
Сюда входят бесполезные и лишние участки кода, от которых можно избавиться без негативных последствий для функциональности программы:
- большое количество комментариев, в том числе поясняющих очевидные моменты:
- дублирование кода – два и более одинаковых участка в разных местах программы;
- ленивые классы – выполняют маловажные задачи, на которых можно «сэкономить»;
- мертвый код – неиспользуемые параметры, переменные, поля, методы или классы;
- классы данных – содержат в себе только поля и наиболее примитивные методы.
Регулярное устранение замусоривателей делает код более чистым и простым для восприятия как самим автором, так и другими разработчиками, которые занимаются поддержкой приложения.
Раздувальщики
Категория включает элементы кода, классы и методы, которые в ходе разработки программы увеличиваются до таких больших размеров, что с ними больше нельзя продуктивно работать:
- длинный метод, в состав которого входит больше десяти строк;
- использование элементарных типов вместо маленьких объектов;
- злоупотребление константами с целью кодирования данных;
- большой класс, содержащий много методов, полей и строк кода;
- применение констант для присвоения названий полям в массивах;
- длинные списки параметров – больше четырех у одного метода;
- одинаковые группы с переменными в различных частях кода.
Признаки из этой категории особенно опасны тем, что проявляются не сразу, а постепенно нарастают в процессе эволюции приложения. В какой-то момент они становятся острой проблемой.
Нарушители ООП
Если разработчик не полностью либо неправильно использует возможности, которые ему дает объектно-ориентированное программирование, часто появляются перечисленные проблемы:
- использование в коде оператора «switch» или последовательности из нескольких «if»;
- наличие временных полей, которые большую часть времени не содержат данных;
- подклассы используют малую часть унаследованных свойств и методов суперкласса;
- несколько классов выполняют аналогичные функции, но наделены разными методами.
Частое появление перечисленных ошибок свидетельствует о необходимости лучше разобраться в свойствах объектно-ориентированного программирования. Это сократит время на рефакторинг.
Утяжелители изменений
Сюда входят проблемы, из-за которых невозможно изменить один участок кода, не внося при этом корректировки в других его местах, а именно:
- Стрельба дробью. Любые модификации кода заставляют разработчика отредактировать большое количество классов.
- Расходящиеся модификации. Редактирование конкретного класса невозможно без изменения разных методов.
- Параллельное наследование. Создание подкласса для одного класса требует выполнение аналогичной операции со связанным классом.
Обилие перечисленных утяжелителей делает развитие программы более дорогим и сложным.
Другие признаки
Кроме перечисленных выше есть еще ряд ситуаций, в которых необходимо проводить рефакторинг:
- метод обращается к данным, относящимся к другому объекту гораздо чаще, чем к своим;
- класс выполняет всего одно действие, а именно передает работу другому классу;
- неуместная близость – один класс оперирует методами и служебными полями другого.
Эти проблемы возникают в случае, когда разработчик неправильно оперирует связями в ООП.
Эффективные методы рефакторинга
Способов провести рефакторинг кода так же много, как и поводов для выполнения этой процедуры. Задача разработчика – провести детальную ревизию программы, определить наличие проблем, описанных ранее, и на основе обнаруженных признаков выбрать тот или иной метод рефакторинга.
Составление методов
Речь идет о целой группе способов, призванных уменьшить длину и сложность методов, устранить дублирование кода и облегчить работу с ним. В первую очередь важно провести работу с переменными:
- Извлечение. Если в коде есть сложное для восприятия выражение, нужно поместить его результат полностью либо по частям в отдельные переменные, которые поясняют суть конструкции.
- Встраивание. Есть временный контейнер, в который сохраняется результат простого выражения? Стоит упростить код, заменив этим выражением обращения к переменной.
- Расщепление. Программист использует разные переменные для различных данных – каждый контейнер должен отвечать за конкретное значение.
Если в коде есть фрагмент, который можно сгруппировать, нужно выделить этот участок в новый метод, затем вызвать его вместо старого кода.
Организация данных
Способы рефакторинга кода из этой категории упрощают работу с данными. Суть заключается в замене примитивных типов на классы, которые имеют куда большую функциональность. Методы:
- Самоинкапсуляция поля. Разработчик формирует сеттер и геттер для поля, и в дальнейшем с целью доступа к полю пользуется исключительно ими.
- Значение вместо ссылки. Большое количество одинаковых экземпляров, относящихся к одному классу, заменяется одним объектом-ссылкой.
- Ссылка вместо значения. Неизменяемый и чрезмерно малый объект-ссылку подменяется объектом-значением, чтобы сделать управление его жизненным циклом более простым.
- Замена связей. Добавляется недостающая связь в класс в случае, если нужно использовать возможности двух классов. Иногда рефакторинг заключается в обратном – удаление двухсторонней связи в ситуации, когда взаимное использование функций больше не нужно.
В ходе рефакторинга кода для организации данных разработчик должен стремиться к уменьшению связанности классов. Это улучшает их переносимость и повышает шансы повторного применения.
Упрощение условий
Одна из задач рефакторинга – минимизировать количество условных операторов в коде. Варианты:
- разбить сложный оператор на части, используя отдельные функции «then» и «else»;
- объединить одинаковые условия, которые приводят к одному и тому же результату;
- вынести все одинаковые фрагменты кода за пределы рамок условного оператора;
- заменить флаг для булевских выражений функциями «break», «continue» и «return».
В языках с поддержкой объектно-ориентированного программирования условный оператор, который выполняет различные действия в зависимости от свойств или типа объекта, рекомендуется заменять полиморфизмом. Для этого программист создает подклассы в соответствии с ветками условия, затем делает для них общий метод и помещает внутрь него код из той ветви, которая для него подходит. Заключительный аккорд – замена условного оператора на вызов этого метода.
Что еще рефакторить
Есть еще несколько методов, использование которых поможет провести грамотный refactoring программы и сделать ее структуру более понятной:
- Непонятные имена. Все названия классов, методов, переменных и других конструкций кода должны иметь интуитивно-понятные наименования, по которым можно понять, что именно они делают. Следование правилу сделает структуру понятной для других разработчиков.
- Большие методы и классы. Громоздкие конструкции сокращаются путем вынесения фрагментов в компактные методы и классы, которые правильно ссылаются друг на друга.
- Длинные списки параметров. Здесь работает аналогичное правило, что и выше. Число аргументов сокращается максимум до четырех-пяти, иначе структура программы будет сложной.
- Цепочки сообщений. Метод любого объекта должен ссылаться только на собственные либо переданные извне параметры, а также на те участки кода, к которому есть прямой доступ. Это правило называется законом Деметры. Если в программе это не так, нужно перепроверить все связи и правильно их настроить.
- Статика. Обилие статических переменных увеличивает непредсказуемость программы и делает код более процедурным, нежели объектно-ориентированным. Чтобы избежать этого, разработчик должен инстанцировать объекты, позволяя им управлять данными так, как им нужно.
- Универсальные объекты. Такие конструкции, имеющие доступ к другим универсальным участкам программы, нарушают закон Деметры и увеличивают связность системы. Это не идет на пользу приложению, поэтому в процессе рефакторинга важно инжектировать только необходимые зависимости.
Увеличить эффективность и скорость рефакторинга помогут хорошие тесты – функциональные, интеграционные и unit-тесты. Необходимо перепроектировать программу небольшими итерациями, после каждого такого шага проводить тестирование, и, если все в порядке, продолжать процедуру.
Выше приведены основные идеи того, как провести refactoring кода и устранить большинство типичных ошибок. Грамотный разработчик должен делать рефакторинг регулярно, а не от случая к случаю. Только так можно упростить задачу, сократить время и силы, необходимые на реструктуризацию программы.