Why Coupling Is the Enemy of Maintainability

Coupling — measured by the CBO metric in the CK suite — describes how many other classes a given class depends on. When coupling is high, changes ripple unpredictably through the codebase, unit tests require complex setup, and reuse becomes nearly impossible. Reducing coupling is one of the highest-return refactoring investments you can make in a Java project.

Identifying High-Coupling Classes

Before you refactor, measure. Run CKJM or SonarQube to get CBO scores across your project. Sort by CBO descending and focus on the top offenders first. Common symptoms of excessive coupling include:

  • A single class that imports from dozens of packages.
  • Methods that accept or return many different concrete types.
  • Classes that are referenced in nearly every other module.
  • Tests that require enormous mock setups just to exercise one method.

Technique 1: Program to Interfaces, Not Implementations

The most impactful change you can make is replacing concrete class dependencies with interface dependencies. Instead of declaring ArrayList<Order> orders, declare List<Order> orders. Instead of depending on MySQLOrderRepository, depend on an OrderRepository interface.

This single change immediately reduces CBO, enables easy substitution in tests, and enforces the Dependency Inversion Principle.

Technique 2: Apply Dependency Injection

When a class creates its own dependencies via new, it is tightly coupled to the concrete implementation. Extract that construction to a factory, a Spring @Configuration class, or a constructor parameter. This is called Dependency Injection (DI).

// Before — tightly coupled
public class OrderService {
    private final EmailNotifier notifier = new EmailNotifier();
}

// After — loosely coupled via DI
public class OrderService {
    private final Notifier notifier;
    public OrderService(Notifier notifier) {
        this.notifier = notifier;
    }
}

Technique 3: Introduce a Facade

If one class calls into many subsystems, introduce a Facade that encapsulates those interactions. The calling class now depends only on the Facade, dramatically reducing its CBO. The Facade absorbs the coupling and exposes a simpler, higher-level API.

Technique 4: Apply the Law of Demeter

The Law of Demeter (or "principle of least knowledge") states that a method should only call methods on:

  • The object itself.
  • Objects passed as parameters.
  • Objects it creates.
  • Direct component objects.

Chains like order.getCustomer().getAddress().getCity() create hidden coupling to three different classes. Refactor these chains by adding a dedicated method on the closest object: order.getCustomerCity().

Technique 5: Use Events and Observers for Cross-Module Communication

When Module A needs to notify Module B about something, direct method calls create coupling. Replace them with an event bus or the Observer pattern. Module A publishes an event; Module B subscribes to it. Neither needs to know the other exists.

In Spring applications, ApplicationEventPublisher provides this capability with minimal boilerplate.

Measuring Your Progress

After each refactoring step, re-run your metrics. A successful coupling-reduction effort should show:

  • Lower average CBO across the refactored classes.
  • Simpler unit tests with fewer mocks.
  • Smaller import sections at the top of each file.

Refactoring coupling is iterative. Don't try to fix everything at once — pick the class with the worst CBO score, apply one technique, validate with tests, then move to the next. Steady, measured progress beats a risky big-bang rewrite every time.