Рефакторинги, которыми вы действительно воспользуетесь
Что это такое
Большинство настоящих улучшений приходит из небольшого набора названных преобразований: выделить функцию, честно переименовать, перенести код в модуль, которому он принадлежит, заменить цепочку условий ясной диспетчеризацией и удалить то, что никто не использует. У каждого есть форма, которую можно применить механически, — именно это и делает их безопасными: вы не изобретаете проектирование, а применяете известный ход с известным эффектом.
Почему это важно
Названные ходы превращают смутное намерение («здесь беспорядок») в последовательность конкретных шагов, которые можно выполнить в любой кодовой базе, на любом языке и без сеанса проектирования. Это ещё и словарь, которым люди пользуются на ревью: «выдели валидацию» точно говорит коллеге, что менять и насколько крупным будет изменение. Знание горстки приёмов, несущих основную тяжесть, позволяет улучшать код в десятиминутные промежутки, а не ждать тихой недели, которая так и не наступает.
Ключевые идеи
Выделяйте и встраивайте функции
Выделение берёт фрагмент, которому нужно имя, и превращает его в функцию с именем, говорящим, что она делает. Это самый полезный ход, и к нему тянутся, когда функцию трудно пересказать; обратный ход, встраивание, столь же законен — когда функция больше не оправдывает своё имя, возврат её внутрь убирает лишний переход без смысла.
// до
const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
// после
const total = calculateTotal(items);
Переименовывайте и переносите
Переименование ничего не стоит, когда инструменты честны, и окупается при каждом чтении. Перенос кода в модуль, которому принадлежит понятие, — та же идея в большем масштабе, и обычно это и есть лечение, когда зависимость смотрит не туда: код лежит не там, поэтому импорт, который должен быть разрешён, выглядит запрещённым.
invoice/pricing.ts <- правило живёт вместе с понятием
orders/order.ts <- импортирует его, а не владеет им
Заменяйте цепочки условий диспетчеризацией
Длинная цепочка условий, проверяющих одно и то же значение, — сигнал, что у вариации есть имя. Замена её поиском по таблице, небольшим отображением из случая в обработчик или полиморфным типом делает добавление следующего случая однострочным изменением в одном месте вместо правки посреди цепочки.
const rates = { retail: 1.2, wholesale: 1.07 };
const price = base * (rates[channel] ?? 1);
Введите объект-значение для связанных значений
Когда несколько параметров путешествуют вместе — сумма и её валюта, начало и конец, хост и порт, — они обычно хотят быть одной вещью. Объект-значение даёт группе имя, даёт валидации один дом и убирает риск того, что две части передадут в неверном порядке.
class DateRange {
constructor(readonly start: Date, readonly end: Date) {
if (end < start) throw new Error("end before start");
}
}
Удаляйте и опирайтесь на тесты
Самый ценный рефакторинг часто — удаление: мёртвый код, неиспользуемые параметры, ветка, до которой больше не добраться, флаг, который всегда истинен. Удаление безопасно ровно тогда, когда тесты зелёные, а инструменты не сообщают о вызывающих, и это изменение надёжнее всего делает файл проще для чтения; предпочитайте убирать, когда возможны оба хода.
grep по вызывающим -> никого -> удалить -> тесты всё ещё зелёные
Практика
Сделайте изменение локальным
Найдите функцию с цепочкой условий по одному значению. Замените цепочку таблицей или небольшой диспетчеризацией, затем добавьте новый случай и убедитесь, что он занял одну строку.
Готово, когда: новый случай требует одного добавления и правки существующих веток не нужны.
Удалите что-нибудь
Найдите параметр, ветку или экспортируемую функцию без вызывающих. Удалите её и прогоните весь набор тестов и проверку типов.
Готово, когда: ничего не проваливается, никто не импортирует удалённое имя, а файл стал короче.
Частые ошибки
- Выделение фрагмента, используемого один раз и без имени — лишний переход без смысла, от которого вызывающий код труднее читать; исправить: выделяйте, когда у фрагмента есть имя, которое стоит произнести.
- Переименование без обновления строковых ссылок — рефлексия, конфигурация и тесты хранят старое имя и падают во время выполнения; исправить: ищите по всему репозиторию, а не только по коду.
- Замена условий таблицей диспетчеризации, скрывающей валидацию — неверный ввод молча проваливается в значение по умолчанию; исправить: сохраните явный случай отказа или возбуждайте ошибку на неизвестном ключе.
- Перенос кода без исправления направления зависимостей — слоистость выглядит правильной, а циклы остаются; исправить: решите, какой модуль владеет понятием, и переносите вызовы.
- Рефакторинг и добавление функции одним шагом — прогон тестов больше не различает их; исправить: сначала завершите структурное изменение, потом добавляйте поведение.
Что читать дальше
- Безопасные шаги рефакторинга (01-cs-fundamentals/19-refactoring/01-safe-refactoring-steps) — процедура, в рамках которой выполняются эти ходы, поэтому читайте её первой.
- Чистый код (01-cs-fundamentals/08-clean-code) — планка качества, говорящая, когда остановиться.
- Переиспользование кода (01-cs-fundamentals/16-code-reusability) — когда выделение стоит сделать общим, а не оставить локальным.