Капитальный ремонт или перепланировку хоть раз в жизни, пожалуй, делал каждый. Трубы и проводка изнашиваются, отделочные материалы и предметы интерьера устаревают, расположение комнат перестает быть удобным. Рефакторинг сравним с капитальным ремонтом и предполагает перепроектирование и исправление кода. Меняется внутренняя структура программы, в то время как поведение остается прежним.
Рефакторинг кода часто требуется, потому что:
- изменяется архитектура;
- необходимо масштабировать решение;
- требуется увеличить производительность;
- первая версия была сделана на скорую руку или «как можно дешевле»;
- появилась новая версия компилятора или библиотек;
- прошлые программисты могли допускать архитектурные ошибки;
- по мере эксплуатации становится понятно, что нужны новые функции.
Низкокачественный код отличается громоздкостью и нечитаемостью, хранением паролей в 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 | Потребление центрального процессора на ожидание. | |||
25 | Некорректное распараллеливание задач или не использование потоков. | |||
26 | Отдельную ветвь кода для частных случаев. | |||
27 | Самописный код вместо использования библиотеки. | |||
28 | Недостаточное комментирование кода или чрезмерное комментирование очевидных участков. |
Отказ от рефакторинга приводит к тому, что любое мелкое вмешательство в код повлечет череду ошибок и исправлений. Вторым неприятным последствием могут быть повторяющиеся изредка критические ошибки, которые будет крайне сложно воспроизвести, диагностировать и отладить. И наконец, постепенное добавление новых функций приведет к наслоению функционала, что, в свою очередь, негативно скажется на работе программы и будет вызывать недовольство пользователей.
Чистый код
Чистый код — условное понятие программистов о том, как правильно кодить. Зачастую определение звучит как-то так: «это код, который легко читается, понятен и хорошо организован». Такой код содержит минимум дубликатов, свободно сопровождается, просто тестируется, его функциональность можно легко расширять. У чистого кода есть несколько отличительных черт.
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 | Принимайте долговые решения осознанно. Когда приходится залезть в долги для достижения краткосрочных целей, помните — долг проявится в будущем. |
Лидеры индустрии инвестируют значительно больше ресурсов в управление техническим долгом, чтобы создать надежный софт, который прослужит много лет.
Завершая тему, еще раз рекомендую при создании и эксплуатации программных приложений помнить о рефакторинге, чистом коде, техническом долге. Чистый код упрощает доработку приложения на всех стадиях развития. Технический долг возникает из-за ускоренной реализации в ущерб качеству продукта. Рефакторинг, в свою очередь, позволяет исправить накопившиеся дефекты.