Сталкивался ли с загроможденными методами
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Загромождённые методы и рефакторинг
Да, абсолютно сталкивался. Загромождённые методы (bloated methods) — одна из самых распространённых проблем в legacy коде. Это методы, которые делают слишком много, содержат множество условий, вложенных циклов и логики разного уровня абстракции.
Примеры проблемы
Вот типичный загромождённый метод из реального проекта:
public void processOrder(Order order) {
// Валидация
if (order == null) throw new IllegalArgumentException("Order is null");
if (order.getItems().isEmpty()) throw new IllegalArgumentException("Empty items");
// Расчёт цены
double total = 0;
for (OrderItem item : order.getItems()) {
if (item.getQuantity() <= 0) continue;
double itemPrice = item.getPrice() * item.getQuantity();
if (item.isDiscounted()) {
itemPrice = itemPrice * 0.9;
}
total += itemPrice;
}
// Применение налога
if (order.getCountry().equals("USA")) {
total = total * 1.08;
} else if (order.getCountry().equals("UK")) {
total = total * 1.20;
}
// Отправка письма
String recipient = order.getCustomer().getEmail();
String subject = "Order #" + order.getId();
String body = "Your order total: $" + total;
emailService.send(recipient, subject, body);
// Сохранение в БД
order.setTotal(total);
order.setStatus("PROCESSED");
orderRepository.save(order);
// Логирование
logger.info("Processed order " + order.getId());
}
Этот метод делает 6 разных вещей. Это нарушает принцип Single Responsibility Principle (SRP).
Как я решаю такие проблемы
1. Извлечение методов (Extract Method)
Разбиваю логику на отдельные методы:
public void processOrder(Order order) {
validateOrder(order);
double total = calculateOrderTotal(order);
applyTax(order, total);
order.setTotal(total);
order.setStatus("PROCESSED");
orderRepository.save(order);
sendConfirmationEmail(order, total);
logOrderProcessing(order);
}
private void validateOrder(Order order) {
if (order == null) throw new IllegalArgumentException("Order is null");
if (order.getItems().isEmpty()) throw new IllegalArgumentException("Empty items");
}
private double calculateOrderTotal(Order order) {
return order.getItems().stream()
.filter(item -> item.getQuantity() > 0)
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.map(price -> item.isDiscounted() ? price * 0.9 : price)
.sum();
}
private void applyTax(Order order, double total) {
double taxRate = getTaxRate(order.getCountry());
total = total * (1 + taxRate);
}
private double getTaxRate(String country) {
return switch(country) {
case "USA" -> 0.08;
case "UK" -> 0.20;
default -> 0.0;
};
}
private void sendConfirmationEmail(Order order, double total) {
String recipient = order.getCustomer().getEmail();
emailService.send(recipient,
"Order #" + order.getId(),
"Your order total: $" + total);
}
private void logOrderProcessing(Order order) {
logger.info("Processed order {}", order.getId());
}
2. Извлечение класса (Extract Class)
Логика может быть слишком сложной даже для методов. Тогда выделяю отдельные классы:
public class OrderProcessor {
private final OrderValidator validator;
private final OrderCalculator calculator;
private final OrderRepository repository;
private final EmailService emailService;
public void processOrder(Order order) {
validator.validate(order);
OrderTotal total = calculator.calculate(order);
order.setTotal(total.getAmount());
order.setStatus("PROCESSED");
repository.save(order);
emailService.sendConfirmation(order, total);
}
}
public class OrderValidator {
public void validate(Order order) {
if (order == null) throw new IllegalArgumentException("Order is null");
if (order.getItems().isEmpty()) throw new IllegalArgumentException("Empty items");
}
}
public class OrderCalculator {
public OrderTotal calculate(Order order) {
double subtotal = calculateSubtotal(order);
double tax = calculateTax(order, subtotal);
return new OrderTotal(subtotal, tax);
}
private double calculateSubtotal(Order order) {
return order.getItems().stream()
.filter(item -> item.getQuantity() > 0)
.mapToDouble(this::calculateItemPrice)
.sum();
}
private double calculateItemPrice(OrderItem item) {
double price = item.getPrice() * item.getQuantity();
return item.isDiscounted() ? price * 0.9 : price;
}
private double calculateTax(Order order, double subtotal) {
double rate = getTaxRate(order.getCountry());
return subtotal * rate;
}
}
3. Удаление условной логики (Replace Conditional with Polymorphism)
// Вместо if/else
public interface TaxCalculator {
double calculateTax(double amount);
}
public class USATaxCalculator implements TaxCalculator {
@Override
public double calculateTax(double amount) {
return amount * 0.08;
}
}
public class UKTaxCalculator implements TaxCalculator {
@Override
public double calculateTax(double amount) {
return amount * 0.20;
}
}
public class TaxCalculatorFactory {
public TaxCalculator getTaxCalculator(String country) {
return switch(country) {
case "USA" -> new USATaxCalculator();
case "UK" -> new UKTaxCalculator();
default -> amount -> 0;
};
}
}
4. Использование Stream API и функциональной парадигмы
Вместо imperативных циклов — declarative подход:
// Плохо
List<Integer> prices = new ArrayList<>();
for (OrderItem item : order.getItems()) {
if (item.getQuantity() > 0) {
prices.add(item.getPrice());
}
}
// Хорошо
List<Integer> prices = order.getItems().stream()
.filter(item -> item.getQuantity() > 0)
.map(OrderItem::getPrice)
.toList();
Признаки загромождённого метода
- Больше 20 строк кода — красный флаг
- Больше 3 параметров — сложновато тестировать
- Больше 3 уровней вложенности — трудно понять
- Несколько ответственностей — нарушает SRP
- Сложно назвать одним глаголом — значит, делает много
- Трудно тестировать — признак плохого дизайна
Мой подход при code review
- Спрашиваю: "Что этот метод делает?"
- Если ответ содержит несколько "и", "также", "затем" — нужен рефакторинг
- Предлагаю простые улучшения: Extract Method
- Потом более сложные: Extract Class, Polymorphism
- Всегда пишу тесты после рефакторинга
Инструменты
IDE помогают:
- IntelliJ IDEA: Refactor → Extract Method/Class
- SonarQube: находит методы с high complexity
- Metrics плагины: показывают Cyclomatic Complexity
Вывод
Загромождённые методы — это техдолг. Лучше потратить время на рефакторинг сразу, чем потом месяцы искать баги в 300-строчном методе. Clean code — это не эстетика, это инвестиция в поддерживаемость и скорость разработки.