Первый шаг — обнаружение проблемных мест или «запахов кода». Классические запахи, описанные Фаулером, включают Длинный метод (Long Method), Большой класс (Large Class), Повторяющийся код (Duplicated Code), Цепочку вызовов (Message Chains) и Излишнюю сложность (Speculative Generality). Современные IDE, такие как IntelliJ IDEA, имеют встроенные инспекции для автоматического обнаружения многих из них. Например, метод, занимающий несколько экранов, или класс, знающий слишком много о внутреннем устройстве других классов, — явные кандидаты на изменения.
Прежде чем начать, убедитесь в наличии надежного набора автоматических тестов (юнит- и интеграционных). Они служат вашей сеткой безопасности. Следуйте «правилу трех шагов»: Красный тест (убедитесь, что тесты проходят), Рефакторинг (вносите изменения), Зеленый тест (убедитесь, что тесты по-прежнему проходят). Если тестов нет, начните с написания характеристических тестов (characterization tests), которые фиксируют текущее поведение системы, даже если оно неидеально.
Рассмотрим ключевые приемы рефакторинга на практических примерах на Java.
- Извлечение метода (Extract Method). Это, пожалуй, самый часто используемый прием. Когда фрагмент кода можно сгруппировать и назвать, его стоит вынести в отдельный метод.
System.out.println("=== INVOICE ===");
// Расчет итого
double total = 0;
for (Item item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
double tax = total * 0.2;
double grandTotal = total + tax;
// Печать деталей
System.out.println("Total: " + total);
System.out.println("Tax: " + tax);
System.out.println("Grand Total: " + grandTotal);
}
Стало:
public void printInvoice(Order order) {
printHeader();
double grandTotal = calculateGrandTotal(order);
printTotals(grandTotal);
}
private double calculateGrandTotal(Order order) {
double total = calculateSubtotal(order);
double tax = calculateTax(total);
return total + tax;
}
// ... другие извлеченные методы
- Замена примитива объектом (Replace Primitive with Object). Когда примитивный тип (например, String для email) начинает обрастать логикой валидации и поведения.
// Валидация разбросана в разных местах
if (!customerEmail.contains("@")) { ... }
Стало:
class Email {
private final String address;
public Email(String address) {
validate(address);
this.address = address;
}
private void validate(String address) { ... }
public String getAddress() { return address; }
}
Email customerEmail; // Инкапсулирована логика и валидация
- Объединение условных выражений (Consolidate Conditional Expression) и Замена условия полиморфизмом (Replace Conditional with Polymorphism). Если у вас есть длинные цепочки if-else или switch, зависящие от типа объекта, рассмотрите создание иерархии классов.
if ("US".equals(country)) {
return weight * 0.5;
} else if ("EU".equals(country)) {
return weight * 0.7 + 5.0;
} else {
return weight * 1.2;
}
}
Стало:
interface ShippingCalculator {
double calculate(double weight);
}
class USShippingCalculator implements ShippingCalculator { ... }
class EUShippingCalculator implements ShippingCalculator { ... }
// Создание нужного калькулятора через фабрику
Стратегический рефакторинг, такой как выделение модулей или изменение архитектуры (например, переход от монолита к слоистой или гексагональной архитектуре), требует более масштабного планирования. Здесь полезны такие приемы, как Branch by Abstraction — создание нового абстрактного слоя, позволяющего постепенно мигрировать функциональность со старой реализации на новую, не ломая систему.
Важно помнить, что рефакторинг не должен быть самоцелью. Его стоит проводить в рамках выделенного времени (технического долга), при исправлении багов (если код вокруг сложен для понимания) или при добавлении новой функциональности в область, которую вы уже затрагиваете (правило бойскаута: «оставь код чище, чем ты его нашел»). Автоматизируйте рутинные преобразования с помощью возможностей IDE (Refactor -> Extract method/ variable/ etc.), это минимизирует ошибки.
В заключение, рефакторинг — это непрерывный процесс, культура, а не разовое событие. Инвестируя время в улучшение кода сегодня, вы многократно экономите время и снижаете риски завтра.
Комментарии (10)