Java Omnibus
OOAD & Architecture / OOP foundations / Coupling and Cohesion in Java Design
OOP Foundations Java Order Management
OOP Foundations

Coupling and cohesion are the quickest lenses for judging whether a design will get easier or harder to change.

Coupling and cohesion are not abstract architecture words. They are practical tools for reasoning about change. Cohesion asks whether a unit of code belongs together. Coupling asks how tightly one unit depends on another. OOAD gets clearer when you look at both together instead of treating them as separate metrics.

Why This Topic Matters

Why it matters in real Java systems

In business systems, most pain comes from change amplification. A new refund rule touches order logic, payment handling, shipment reversal, and customer messaging. If a class or package has low cohesion and high coupling, small requests become high-risk edits. That is why this topic belongs early in the learning sequence.

Core Explanation

The design idea behind this topic

High cohesion means a type or module has a clear center of gravity. In OOAD, that usually means one business purpose or one small cluster of closely related concerns.

Low coupling means fewer fragile dependencies. Java systems achieve this through stable interfaces, focused collaborators, better package boundaries, and models that own their own rules.

One of the easiest review questions you can ask is: “If this business rule changes, how many files should need to care?” That question often exposes both coupling and cohesion problems immediately.

Concept Breakdown

Key ideas to hold onto

Concept

High cohesion

A class, module, or package keeps responsibilities that naturally belong together.

Concept

Loose coupling

A unit depends on other units through stable, limited contracts rather than internal detail.

Concept

The two reinforce each other

Better cohesion often reduces coupling because fewer unrelated responsibilities leak outward.

Java Example

Java example in the Order Management domain

Java example: splitting one bloated service into cohesive collaborators
package org.javaomnibus.ecommerce.foundations;

public final class PlaceOrderUseCase {
    private final InventoryReservationPolicy inventoryReservationPolicy;
    private final PaymentAuthorizer paymentAuthorizer;
    private final ConfirmationNotifier confirmationNotifier;

    public PlaceOrderUseCase(
        InventoryReservationPolicy inventoryReservationPolicy,
        PaymentAuthorizer paymentAuthorizer,
        ConfirmationNotifier confirmationNotifier
    ) {
        this.inventoryReservationPolicy = inventoryReservationPolicy;
        this.paymentAuthorizer = paymentAuthorizer;
        this.confirmationNotifier = confirmationNotifier;
    }

    public void handle(Order order) {
        inventoryReservationPolicy.reserve(order);
        paymentAuthorizer.authorize(order);
        confirmationNotifier.notify(order);
    }
}

interface InventoryReservationPolicy { void reserve(Order order); }
interface PaymentAuthorizer { void authorize(Order order); }
interface ConfirmationNotifier { void notify(Order order); }
record Order(String orderId) {}
Code Walkthrough

Step by step

  1. PlaceOrderUseCase stays cohesive: it coordinates one business use case.
  2. InventoryReservationPolicy, PaymentAuthorizer, and ConfirmationNotifier each own one narrow responsibility.
  3. The use case is coupled to abstractions, but the coupling is focused and intentional.
  4. If notification behavior changes, payment logic does not need to change with it.
Real-World Usage

Where this shows up in practice

  • Refactoring service classes that have absorbed unrelated responsibilities
  • Evaluating whether package boundaries match business responsibilities
  • Reviewing whether a use case is coupled to too many technical details
Trade-Offs

When not to over-apply it

Pursuing perfect decoupling can create too many tiny abstractions. The goal is not minimal dependencies at all cost. It is dependencies that line up with stable, meaningful boundaries.

Common Mistakes

Frequent mistakes to watch for

  • Calling a design low-coupled just because it uses interfaces everywhere
  • Splitting responsibilities so aggressively that the model becomes fragmented and hard to follow
  • Ignoring package-level coupling while focusing only on class-level cleanups
  • Leaving orchestration classes with too many unrelated reasons to change
Related Pages

Related concepts


What To Read Next