СДЕЛАЙТЕ СВОИ УРОКИ ЕЩЁ ЭФФЕКТИВНЕЕ, А ЖИЗНЬ СВОБОДНЕЕ

Благодаря готовым учебным материалам для работы в классе и дистанционно

Скидки до 50 % на комплекты
только до

Готовые ключевые этапы урока всегда будут у вас под рукой

Организационный момент

Проверка знаний

Объяснение материала

Закрепление изученного

Итоги урока

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

Категория: Информатика

Нажмите, чтобы узнать подробности

Просмотр содержимого документа
«Рефакторинг программного кода»

Тема 1. Рефакторинг программного кода


- Определение рефакторинга


Рефакторинг представляет собой процесс изменения программной системы, при котором не меняется внешнее поведение кода, но улучшается его внутренняя структура. Улучшение качества кода без изменения его функциональности

В сущности, при проведении рефакторинга кода вы улучшаете его дизайн уже после того, как он написан.

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


- Цель: упростить понимание и модификацию программного обеспечения.


- Задачи рефакторинга


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


  • Устранение дублирования. Рефакторинг помогает обнаружить и объединить повторяющиеся участки кода, что сокращает трудозатраты на их поддержку и вероятность ошибок, появляющихся при необходимости изменить логику в нескольких местах.


  • Упрощение структур. Зачастую код со временем усложняется за счет внесенных дополнений и исправлений. Упрощение может помочь вернуть код к более понятной, поддерживаемой форме.


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


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


- Основные принципы рефакторинга


  1. Малые изменения: Изменения должны быть небольшими и итеративными, чтобы легче отслеживать ошибки.


  1. Постоянное тестирование: После каждого изменения код должен проходить тестирование, чтобы убедиться, что его поведение не изменилось.


  1. Чистый код: Следует стремиться к написанию чистого и понятного кода, используя общепринятые стандарты и паттерны проектирования.


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


  1. Поочередные небольшие изменения. Выполнение преобразований небольшими последовательными шагами позволяет избежать ошибок, которые могут возникнуть при крупных и радикальных переработках.


  1. Надежное покрытие тестами. Перед началом рефакторинга необходимо иметь обширный набор тестов, который покрывает все аспекты функциональности приложения.


  1. Регулярность и документирование. Оптимальный подход — это выполнение преобразований как постоянного и регулярного процесса разработки.

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


- Процесс рефакторинга можно разделить на несколько шагов:


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


  1. Планирование изменений: Разработайте план рефакторинга, определив конкретные изменения и методы, которые будут применены.


  1. Внесение изменений: Реализуйте изменения, следуя принципам и методам рефакторинга. Рефакторинг следует проводить итеративно, внося маленькие изменения за раз. Это помогает избежать серьезных ошибок и облегчает откат изменений в случае их неудачи. К таким шагам относятся: переименование переменных, разбиение больших функций на меньшие, удаление дублированного кода, улучшение структуры условных операторов и применение принципов SOLID.


SOLID — это аббревиатура пяти основных принципов проектирования в объектно‑ориентированном программировании

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


Расшифровка:


  • Single responsibility principle / SRP — принцип единственной ответственности - обозначает, что каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс. Все его сервисы должны быть направлены исключительно на обеспечение этой обязанности. Каждый модуль должен иметь одну единственную ответственность.


  • Open-closed principle / OCP — принцип открытости / закрытости- декларирует, что программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения. Это означает, что эти сущности могут менять свое поведение без изменения их исходного кода.



  • Liskov substitution principle / LSP — принцип подстановки Барбары Лисков - функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа не зная об этом. Дочерний класс должен быть взаимозаменяем с родительским классом.


  • Interface segregation principle / ISP — принцип разделения интерфейса - «клиенты не должны зависеть от методов, которые они не используют». Принцип разделения интерфейсов говорит о том, что слишком «толстые» интерфейсы необходимо разделять на более маленькие и специфические, чтобы клиенты маленьких интерфейсов знали только о методах, которые необходимы им в работе. В итоге, при изменении метода интерфейса не должны меняться клиенты, которые этот метод не используют


  • Dependency inversion principle / DIP — принцип инверсии зависимостей - модули верхних уровней не должны зависеть от модулей нижних уровней, а оба типа модулей должны зависеть от абстракций; сами абстракции не должны зависеть от деталей, а вот детали должны зависеть от абстракций


  1. Тестирование: Проведите тестирование, чтобы убедиться, что поведение кода не изменилось.


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


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


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


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


  • Внесение новых ошибок. Даже незначительные изменения могут привести к новым багам в стабильно работающих ранее частях системы. Без должного тестирования и контроля изменений рефакторинг может неожиданно нарушить работу приложения. Риски повышаются, если в команде нет эффективной практики code review. Необходимость тщательно проверять каждое изменение и иметь надежный набор автоматических тестов становится при этом критически важной.


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



Рефакторинг должен укладываться в рамки установленных сроков и приносить ощутимые преимущества для проекта.


  • Утрата функциональности. Изменения могут случайно привести к потере определенных функций, особенно когда они недостаточно документированы или плохо поняты. Это особенно вероятно, если систему разрабатывало несколько разных команд или если над ней долго работала одна команда без регулярного рефакторинга. Рефакторинг без четкого понимания бизнес-логики и требований к программному продукту повышает риск утраты критически важных фрагментов кода. Поэтому перед проведением рефакторинга стоит тщательно проанализировать и документировать назначение кода.


  • Проблемы совместимости. В процессе преобразований может быть изменена часть кода, от которой зависят другие системы, особенно если они используют API или библиотеки программы. Это может нарушить совместимость с внешними компонентами или сторонними системами, которые рассчитывают на определенное поведение или структуру кода. Важно учитывать все взаимодействия и точки интеграции перед началом рефакторинга. Поддержка обратной совместимости с ранее используемыми интерфейсами часто становится основным приоритетом.


  • Командная дезориентация. Если изменения в коде не коммуницированы должным образом, это может привести к путанице среди членов команды. Без четкого понимания новой структуры или логики программы другие разработчики могут случайно отменить изменения или создать новые конфликты. Рефакторинг должен сопровождаться хорошей внутренней документацией и эффективным общением в команде. Работа в ветках с использованием систем контроля версий помогает отслеживать изменения и предотвращает конфликты в коде.



Причины проведения рефакторинга (стр 85):

(Самостоятельно законспектировать, выделяя основную суть.)


● дублирование кода;

● длинный метод;

● большой класс;

● длинный список параметров;

● расходящиеся модификации

● «завистливые» функции ― это метод, который чрезмерно обращается к данным другого объекта;

● группы данных

● одержимость элементарными типами

● операторы типа switch

● параллельные иерархии наследования

● ленивый класс

● временное поле

● цепочки сообщений

● посредник

● неуместная близость

● альтернативные классы с разными интерфейсами

● неполнота библиотечного класса

● избыточные временные переменные;

● классы данных;

● несгруппированные данные.

● отказ от наследства



- Популярные методы и техники рефакторинга (стр 123)

(Самостоятельно законспектировать, выделяя основную суть.)


●Выделение метода (Extract Method)

●Встраивание метода (Inline Method)

●Встраивание временной переменной (Inline Temp)

●Замена временной переменной вызовом метода (Replace Temp with Query)

●Введение поясняющей переменной (Introduce Explaining Variable)

●Расщепление временной переменной (Split Temporary Variable)

●Удаление присваиваний параметрам (Remove Assignments to Parameters)

●Замена метода объектом методов (Replace Method with Method Object)

●Замещение алгоритма (Substitute Algorithm)


Перемещение функций между объектами

●Перемещение метода (Move Method)

●Перемещение поля (Move Field)

●Выделение класса (Extract Class)

●Встраивание класса (Inline Class)

●Сокрытие делегирования (Hide Delegate)

●Удаление посредника (Remove Middle Man)

●Введение внешнего метода (Introduce Foreign Method)

●Введение локального расширения (Introduce Local Extension)


Организация данных

●Самоинкапсуляция поля (Self Encapsulate Field)

●Замена значения данных объектом (Replace Data Value with Object)

●Замена значения ссылкой (Change Value to Reference)

●Замена ссылки значением (Change Reference to Value)

●Замена массива объектом (Replace Array with Object)

●Дублирование видимых данных (Duplicate Observed Data)

●Замена однонаправленной связи двунаправленной (Change Association to Bidirectional)

●Замена двунаправленной связи однонаправленной (Change Bidirectional Association

to Unidirectional)

●Замена магического числа символической константой (Replace Magic Number with

Symbolic Constant)

●Инкапсуляция поля (Encapsulate Field)

●Инкапсуляция коллекции (Encapsulate Collection)

●Замена записи классом данных (Replace Record with Data Class)

●Замена кода типа классом (Replace Type Code with Class)

●Замена кода типа подклассами (Replace Type Code with Subclasses)

●Замена кода типа состоянием/стратегией (Replace Type Code with State/Strategy)

●Замена подкласса полями (Replace Subclass with Fields)


Упрощение условных выражений

●Декомпозиция условного оператора (Decompose Conditional)

●Консолидация условного выражения (Consolidate Conditional Expression)

●Консолидация дублирующихся условных фрагментов (Consolidate Duplicate Conditional Fragments)

●Удаление управляющего флага (Remove Control Flag)

●Замена вложенных условных операторов граничным оператором (Replace Nested

Conditional with Guard Clauses)

●Замена условного оператора полиморфизмом (Replace Conditional Polymorphism)

●Введение объекта (Introduce Object)

●Введение утверждения (Introduce Assertion)


Упрощение вызовов методов

●Переименование метода (Rename Method)

●Добавление параметра (Add Parameter)

●Удаление параметра (Remove Parameter)

●Разделение запроса и модификатора (Separate Query from Modifier)

●Параметризация метода (Parameterize Method)

●Замена параметра явными методами (Replace Parameter with Explicit Methods)

●Сохранение всего объекта (Preserve Whole Object)

●Замена параметра вызовом метода (Replace Parameter with Method)

●Введение граничного объекта (Introduce Parameter Object)

●Удаление метода установки значения (Re move Setting Method)

●Сокрытие метода (Hide ПMetоhod)

●Замена конструктора фабричным методом (Replace Constructor with Factory

Method)

●Инкапсуляция нисходящего преобразования типа (Encapsulate Downcast)

●Замена кода ошибки исключительной ситуацией (Replace Error Code with Exception)

●Замена исключительной ситуации проверкой (Replace Exception with Test)


Решение задач обобщения

●Подъем поля (Pull Up Field)

●Подъем метода (Pull Up Method)

●Подъем тела конструктора (Pull Up Constructor Body)

●Спуск метода (Push Down Method)

●Спуск поля (Push Down Field)

●Выделение подкласса (Extract Subclass)

●Выделение родительского класса (Extract Superclass)

●Выделение интерфейса (Extract Interface)

●Свертывание иерархии (Collapse Hierarchy)

●Формирование шаблона метода (Form Template Method)

●Замена наследования делегированием (Replace Inheritance with Delegation)

●Замена делегирования наследованием (Replace Delegation with Inheritance)


Крупные рефакторинги

●Разделение наследования (Tease Apart Inheritance)

●Преобразование процедурного проекта в объекты (Convert Procedural Design to

Objects)

● Отделение предметной области от представления (Separate Domain from Presentation)

●Выделение иерархии (Extract Hierarchy)





!!! Задание для самостоятельной работы в подгруппах
(2-4 человка)!!!


Пользуясь дополнительной литературой в открытых информационных источниках, составить презентационные материалы и доклад.


  1. Истоки рефакторинга

  2. Отличие рефакторинга от оптимизации, форматирования и дебаггинга (исправления ошибок)

  3. Когда и зачем проводить рефакторинг

  4. Преимущества рефакторинга

  5. Проблемы, возникающие при проведении рефакторинга

  6. Как часто необходимо проводить рефакторинг

  7. Ситуации, когда рефакторинг не нужен

  8. Какие метрики можно использовать для оценки качества кода до и после рефакторинга

  9. Какие наиболее распространенные ошибки следует избегать при рефакторинге

  10. Подробно рассмотреть имеющиеся инструменты рефакторинга кода.