Цитаты и примечания
Эффективная работа с устаревшим кодом
Майкл Фезерс
Что мне понравилось в этой книге: большое внимание уделяется тому, как применить тестовую систему к унаследованному коду (так что у вас есть подстраховка, чтобы начать вносить в него изменения). Он предлагает некоторые конкретные подходы к поиску «частей унаследованного кода, которые могут окупить усилия по его тестированию», но также есть предупреждение о компромиссах: если унаследованный код не поддается тестированию, усилия, необходимые для существующее покрытие может добавить некоторый риск и потребовать много времени.
Изменение программного обеспечения
Причины смены ПО
- Добавление функции
- Исправление ошибки
- Улучшение дизайна
- Оптимизация использования ресурсов
Улучшение дизайна
- Мы хотим сохранить поведение неизменным
- Мы вносим небольшие структурные изменения, поддерживаемые тестами, чтобы сделать код более понятным, отслеживаемым и изменяемым.
Изменение риска
- Мы не знаем, насколько поведение подвергается риску, когда мы вносим изменения
- Как проверить правильность наших изменений? / не нарушает какое-либо существующее поведение? Введите автоматическое тестирование
- Консервативный подход (меньше изменений) порождает страх перед будущими изменениями.
Работа с обратной связью
Покрытие тестами как подстраховка
Покрытие программного обеспечения означает покрытие его тестами. Когда у нас есть хороший набор тестов для фрагмента кода, мы можем вносить изменения и очень быстро выяснять, были ли эффекты хорошими или плохими.
- Модульные тесты дают обратную связь по мере разработки, поэтому вы можете проводить рефакторинг с гораздо большей безопасностью.
- Разрывая зависимости, мы можем делать более глубокие изменения.
Устаревший алгоритм изменения кода
Зачем ломать зависимости?
Распознавание и разделение
- Распознавание: когда нам нужно получить доступ к вычисляемому значению для проверки.
- Разделение. Если мы не можем поместить код в тестовую систему, здесь мы можем использовать фальшивые соавторы (фальшивые объекты, фиктивные объекты, заглушки и т. д.)
Модель шва
- Это просто точка в коде, где вы можете изменить поведение.
- Вы сами решаете, какое поведение использовать (точка включения соединения)
О рефакторинге
Помните, код — это ваш дом, и вы должны в нем жить.
Класс/метод Sprout: это просто класс или метод, который вы можете использовать вместо исходного класса/метода, когда хотите обновить или добавить функцию.
Обернуть класс/метод: переименовать класс или метод, чтобы можно было обновить или добавить функцию.
Инверсия зависимостей. Еще один инструмент для обновления или добавления функции, помогающий отделить модули или соавторов.
Когда у нас есть тесты, у нас появляется больше возможностей для добавления новых функций. У нас есть прочная основа.
- TDD: полезно сосредоточиться на одной вещи за раз.
- Программирование на основе различий: наследование классов для исправления/изменения поведения. Может помочь в некоторых случаях, но нарушает принцип подстановки Лисков.
Проблемы при попытке использовать Test Harness для класса
- Объекты класса непросто создать
- Зависимости усложняют создание Test Harness (глобальные зависимости делают его еще хуже).
- Конструктор (для класса для тестирования) имеет побочные эффекты и/или страдает от «строительного блоба» (много сложных параметров).
- Необходимо проверить влияние изменения в тестируемом классе. Введите Эскизы эффектов: аналогично диаграммам взаимодействия компонентов, но с использованием методов (внутри того же класса). Таким образом, вы можете четко видеть, как изменение метода влияет на другие методы.
- Глобальные или статические данные также могут усложнить тестирование.
- Точка перехвата: место в коде, где вы можете обнаружить последствия определенного изменения. Полезно при написании тестов.
Тесты документируют фактическое текущее поведение системы.
Рассказывание историй для объяснения архитектуры
- Грубая идея состоит в том, чтобы объяснить 2 или 3 основные идеи архитектуры в качестве первого подхода.
- Вы объясняете, что такое элементы дизайна и как они взаимодействуют
- Ваш первый подход будет намеренно расплывчатым, вы не будете лгать, вы просто будете скрывать некоторую сложность, чтобы иметь возможность объяснить более высокий уровень
- Затем вы можете «детализировать», подробно объясняя концепции.
Начнем с краткого описания. Когда мы упрощаем и вырезаем детали для описания системы, мы действительно абстрагируемся. Часто, когда мы заставляем себя сообщить очень простое представление о системе, мы можем найти новые абстракции.
История дает нам ориентир.
Проблемы с большими классами
- Проблемы планирования задач: чем больше обязанностей у класса, тем больше вам может понадобиться изменить его.
- Низкое сцепление и высокая сплоченность всегда имеют значение, особенно для большого класса.
Лучший подход к разбиению больших классов — определить обязанности, убедиться, что все остальные в команде их понимают, а затем разбить класс по мере необходимости. Когда вы делаете это, вы распределяете риск изменений и можете выполнять другую работу по ходу дела.
Устранение повторяющегося кода
Обнаружение одного и того же дублированного кода повсюду может быть неприятно. Удаление этого дублированного кода должно быть несложным, но всегда есть необходимость в тестовом покрытии в качестве соперничества.
- Удаление дублированного кода требует покрытия тестами
- Это также способ перегонки дизайна.
- Принцип открытости/закрытости: код должен быть открыт для расширения, но закрыт для модификации.
Некоторые другие полезные практики
- Заменить функцию указателем функции
Таким образом вы можете отделить реализацию, упростив ее тестирование/расширение/замену
- Оттолкнуть зависимость
Таким образом (помещая зависимость вниз в иерархии) вы делаете исходный класс абстрактным (по отношению к этой зависимости).
- Подкласс и метод переопределения
Позволяет изменять/расширять исходное поведение, и вы даже можете вызывать исходную реализацию.
- Функция подтягивания
Сделав это, вы можете сосредоточиться на конкретной подтянутой функции (опять же, чтобы протестировать ее или изменить/расширить).