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

Введение

Давайте рассмотрим проект, доставшийся нам в наследство — приложение для интернет-магазина книг. Несмотря на то, что он полнофункциональный и функциональный, кодовая база со временем значительно выросла и остро нуждается в очистке. Мы сталкиваемся со смесью стилей кодирования, переплетенными зависимостями и ограниченным тестовым охватом.

Определение областей для рефакторинга

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

Улучшение структуры кода

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

Давайте реорганизуем это, применив принцип инверсии зависимостей (DIP) из принципов SOLID. Вместо того, чтобы классы напрямую зависели друг от друга, они будут зависеть от абстракций.

До:

public class BookStore {
  private BookInventory inventory = new BookInventory();
  ...
}

После:

public class BookStore {
  private IInventory inventory;
  
  public BookStore(IInventory inventory) {
    this.inventory = inventory;
  }
  ...
}

Теперь BookStore больше не зависит от конкретного класса BookInventory, а зависит от интерфейса IInventory.

Устранение запахов кода

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

Одним из таких примеров является метод placeOrder в классе OrderProcessing. Он отвечает за проверку запасов, расчет цен, применение скидок и создание заказов на доставку. Это классический пример метода, который делает слишком много.

Вынося эти обязанности в их собственные методы, мы делаем код более легким для чтения и тестирования. Например, мы можем извлечь расчет цены в новый метод calculateTotalPrice.

До:

public class OrderProcessing {
  ...
  public Order placeOrder(Customer customer, List<Book> books) {
    // check inventory, calculate prices, apply discounts, create order
  }
  ...
}

После:

public class OrderProcessing {
  ...
  public Order placeOrder(Customer customer, List<Book> books) {
    // check inventory, apply discounts, create order
  }
  
  private BigDecimal calculateTotalPrice(List<Book> books) {
    // calculate prices
  }
  ...
}

Улучшение тестового покрытия

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

Внедряя тесты на нескольких уровнях (блок, интеграция и система) и гарантируя покрытие всех критических путей, мы можем с уверенностью проводить рефакторинг. Например, мы ввели модульные тесты для всех новых методов, извлеченных при рефакторинге. Мы использовали JUnit, популярную среду тестирования для Java.

@Test
public void testCalculateTotalPrice() {
  OrderProcessing op = new OrderProcessing();
  List<Book> books = Arrays.asList(new Book("Book1", BigDecimal.valueOf(10)), new Book("Book2", BigDecimal.valueOf(20)));
  
  BigDecimal totalPrice = op.calculateTotalPrice(books);
  
  assertEquals(BigDecimal.valueOf(30), totalPrice);
}

Заключение

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

Рефакторинг может занять много времени и даже пугать при работе с крупными проектами, но при систематическом подходе, правильных инструментах и ​​смелости улучшить тестовое покрытие вы можете значительно улучшить качество своего кода. Помните: «Рефакторинг — это не событие, а часть вашего повседневного процесса разработки».

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

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

  1. Рефакторинг в IntelliJ IDEA
  2. SonarQube
  3. Принципы SOLID в Java

Понравилось читать? Еще не являетесь участником Medium? Вы можете поддержать мою работу напрямую, зарегистрировавшись по моей реферальной ссылке здесь. Это быстро, просто и не требует дополнительных затрат. Спасибо за вашу поддержку!