High cohesion
A class, module, or package keeps responsibilities that naturally belong together.
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.
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.
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.
A class, module, or package keeps responsibilities that naturally belong together.
A unit depends on other units through stable, limited contracts rather than internal detail.
Better cohesion often reduces coupling because fewer unrelated responsibilities leak outward.
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) {}
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.