Рефакторинг кода

Рефакторинг кода

24 августа 2023

Капитальный ремонт или перепланировку хоть раз в жизни, пожалуй, делал каждый. Трубы и проводка изнашиваются, отделочные материалы и предметы интерьера устаревают, расположение комнат перестает быть удобным. Рефакторинг сравним с капитальным ремонтом и предполагает перепроектирование и исправление кода. Меняется внутренняя структура программы, в то время как поведение остается прежним.

Рефакторинг кода часто требуется, потому что:

  • изменяется архитектура;
  • необходимо масштабировать решение;
  • требуется увеличить производительность;
  • первая версия была сделана на скорую руку или «как можно дешевле»;
  • появилась новая версия компилятора или библиотек;
  • прошлые программисты могли допускать архитектурные ошибки;
  • по мере эксплуатации становится понятно, что нужны новые функции.
Велосипед
Изобретение велосипеда
Восклицательный знак в заштрихованном круге

Низкокачественный код отличается громоздкостью и нечитаемостью, хранением паролей в cookies, сложностью URL, именованием переменных на разных языках.

Рефакторинг исправляет.

01 разнородный стиль треугольник круг квадрат Разнородный стиль именования переменных, методов, классов.
02 дублирование кода два скрипта Дублирование кода.
03 длинный класс Длинные методы и классы.
04 перегруженный список Перегруженный список параметров функций и методов.
05 длинные блоки схема Длинные блоки ветвления.
06 магический знак Магические значения.
07 непонятная аббревиатура Использование непонятных аббревиатур.
08 жесткое допущение Жесткое кодирование допущений.
09 чрезмерное конфигурирование Чрезмерное конфигурирование.
10 запутанный код Запутанный код.
11 уровни абстракции круг гептагон треугольник Чрезмерное количество уровней абстракции.
12 зависимый класс Чрезмерную взаимозависимость классов друг от друга.
13 нет проверки перечеркнутый список Отсутствие проверки входных данных.
14 перегруженный класс Перегрузку классов и компонентов разнородным функционалом.
15 обобщенный код Чрезмерную обобщенность кода.
16 переменные в полях Хранение временных значений в полях.
17 классы посредники Классы-посредники.
18 нет изоляции класса Недостаток изоляции классов.
19 неуместное наследование Неуместное наследование классов.
20 наследование делегирование Наследование вместо делегирования.
21 логика вне классов Логику вне классов предметной области.
22 прямые вызовы Прямые вызовы между далеко расположенными частями системы.
23 неиспользуемый код Неиспользуемый код.
24 потребление cpu Потребление центрального процессора на ожидание.
25 нет параллельных cpu Некорректное распараллеливание задач или не использование потоков.
26 отдельная ветвь Отдельную ветвь кода для частных случаев.
27 библиотека Самописный код вместо использования библиотеки.
28 мало комментарий Недостаточное комментирование кода или чрезмерное комментирование очевидных участков.
Дуб на подпорке
Подпорка 600-летнего английского дуба Псково-Печерского монастыря

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

Чистый код

Чистый код — условное понятие программистов о том, как правильно кодить. Зачастую определение звучит как-то так: «это код, который легко читается, понятен и хорошо организован». Такой код содержит минимум дубликатов, свободно сопровождается, просто тестируется, его функциональность можно легко расширять. У чистого кода есть несколько отличительных черт.

01 Чистый код - тесты Чистый код проходит все тесты.
02 Чистый код очевиден Чистый код очевиден для других программистов.
03 Чистый код - дубли Чистый код содержит минимальное количество дублирования.
04 Чистый код легко поддерживать Чистый код проще и дешевле поддерживать.

Когда дело касается разработки программного обеспечения, существуют принципы и техники, которые помогают создавать качественный код: SOLID, YAGNI, KISS и DRY. Также существуют такие методологии как Driven Development.

Техника SOLID — аббревиатура, которая означает пять принципов проектирования объектно-ориентированных программ. Техника направлена на создание модульного кода, упрощение поддержки и расширения функционала, уменьшение связности и повышение гибкости при изменении.

  • Принцип единственной ответственности (Single Responsibility Principle, SRP). Утверждает, что каждый объект должен иметь только одну ответственность, что делает код понятным и упрощает сопровождение.
  • Принцип открытости/закрытости (Open/Closed Principle, OCP). Приложение должно быть открыто для расширения и закрыто для модификации, что не позволит сломать готовый функционал.
  • Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP). Говоря простыми словами, базовый класс должен быть заменяемым на любой из его подтипов. К примеру, вместо общего класса «животное» (Animal) можно подставить как «слон» (Elephant), так и «мышь» (Mouse).
  • Принцип разделения интерфейса (Interface Segregation Principle, ISP). Согласно принципу, пользователи не должны зависеть от ненужных интерфейсов. Каждый интерфейс должен иметь только методы, которые необходимы его клиентам.
  • Принцип инверсии зависимостей (Dependency Inversion Principle, DIP). Принцип утверждает, что высокоуровневые модули не должны зависеть от низкоуровневых модулей. Вместо этого вместе они должны зависеть от абстракций.

Техники SOLID предназначены для решения конкретных проблем, например, принцип SRP решает проблему пересечения ответственностей между классами. Принципы тесно связаны с паттернами проектирования: «стратегия» (Strategy), «декоратор» (Decorator), «фабрика» (Factory), «адаптер» (Adapter), «мост» (Bridge) и т.д.

Восклицательный знак в заштрихованном круге

Паттерны проектирования рассмотрены в отдельной статье.

Принципы YAGNI и KISS (You Aren’t Gonna Need It — «Вам это не понадобится» и Keep It Simple, Stupid — «Делай проще, дурачок») схожи между собой, поскольку направлены на создание максимально простого и понятного кода. Их использование позволяет сосредоточиться на написании необходимого функционала и избежать лишней сложности. Принцип YAGNI призывает разработчиков не писать код, который сейчас не нужен. Практика позволяет сосредоточиться на том функционале, который нужен, и не тратить время на продумывание всей архитектуры. KISS, в свою очередь, призывает «делать проще», он декларирует простоту системы в качестве основной цели или ценности. Указанные принципы имеют взаимосвязь с паттернами проектирования: «фасад» (Facade), «компоновщик» (Composite), «медиатор» (Mediator) и т.д.

Принцип DRY (Don’t Repeat Yourself — «Не повторяйся») призывает программистов не повторяться при написании кода. Повторение кода приводит к его удлинению и увеличению сложности, что усложняет понимание другими программистами. Каждый раз, когда понадобится использовать определенный функционал, используйте готовый и проверенный код. Это помогает уменьшить количество ошибок в программе и сократить время разработки. Для решения проблемы повторения кода используются паттерны проектирования: «фабричный метод» (Factory Method), «одиночка» (Singleton), «прототип» (Prototype) и т.д.

Методологии Driven Development, также известные как Test Driven Development (TDD) и Domain Driven Design (DDD), — два популярных подхода к разработке программного обеспечения. Оба подхода имеют свои преимущества и недостатки, но общую цель — создание надежного программного обеспечения.

TDD основана на написании тестов перед написанием кода. Этот подход позволяет разработчикам убедиться, что их код работает правильно и позволяет быстро выявлять ошибки до того, как они приведут к серьезным проблемам. Самое главное в подходе TDD — быстрое выявление и исправление ошибок.

DDD уделяет особое внимание бизнес-логике приложения. Разработчики строят модели, которые отражают реальный мир: объекты предметной области и взаимодействия между ними. Также DDD включает в себя несколько ключевых концепций, таких как «сущность» (Entity), «объект-значение» (Value Object), «агрегат» (Aggregate), «репозиторий» (Repository).

TDD и DDD — это не просто набор концепций и правил, а, скорее, подход к разработке программного обеспечения. В то же время принципы SOLID, YAGNI, KISS и DRY — это набор техник, которые помогают кодеру оптимизировать код, уменьшать связность, легче править, избегать сложности и повторений. Понимание техник вместе с подходами TDD и DDD помогает разработчикам создать качественное и надежное программное обеспечение, которое покрывает потребности бизнеса.

Технический долг

Восклицательный знак в заштрихованном круге

Комментарий заказчика: «…меня не интересует качество кода, главное пишите дешевле и побыстрее».

Технический долг — понятие, описывающее ситуацию, когда разработчики выбирают неоптимальные решения ради быстрого достижения целей, не учитывая долгосрочное влияние на проект. Другими словами, технический долг — последствия компромиссов в процессе разработки программного обеспечения или инфраструктуры, которые приводят к задержкам, отказам в работе, сбоям и перерасходу ресурсов в будущем. Причины возникновения долга.

01 Давление со стороны бизнеса Давление со стороны бизнеса.
02 Проблемы архитектуры Проблемы архитектуры.
03 Неудачные дизайнерские решения Неудачные дизайнерские решения.
04 Низкая квалификация Низкая квалификация программистов.
05 Отсутствие достаточного тестирования Отсутствие достаточного тестирования.
06 Откладывание рефакторинга Откладывание рефакторинга.
07 Отсутствие документации Отсутствие документации.
08 Отсутствие взаимодействия между членами команды Отсутствие взаимодействия между членами команды.
09 Отсутствие контроля за соблюдением стандартов Отсутствие контроля за соблюдением стандартов.
10 Неправильное использование сторонних библиотек Неправильное использование сторонних библиотек.

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

01 Основные принципы Следуйте принципам и техникам написания чистого кода. Используйте SOLID, YAGNI, KISS и DRY, чтобы получить качественный и эффективный код
02 Стадии разработки Должное внимание тестированию! Регулярный прогон автоматизированных тестов выявляет и помогает устранять ошибки на любой стадии разработки.
03 Регулярный рефакторинг Регулярно проводите рефакторинг кода, который устранит недостатки, обеспечит единообразие кода, упростит поддержку и «читаемость» кода другими разработчиками.
04 Долговые решения Принимайте долговые решения осознанно. Когда приходится залезть в долги для достижения краткосрочных целей, помните — долг проявится в будущем.

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

Завершая тему, еще раз рекомендую при создании и эксплуатации программных приложений помнить о рефакторинге, чистом коде, техническом долге. Чистый код упрощает доработку приложения на всех стадиях развития. Технический долг возникает из-за ускоренной реализации в ущерб качеству продукта. Рефакторинг, в свою очередь, позволяет исправить накопившиеся дефекты.