Coupling: Minimize What a Module Needs to Know
What This Concept Is
Coupling is how much one module must know about another in order to work. Two modules are tightly coupled when a change in one forces a change in the other. They are loosely coupled when each can change independently as long as a narrow, agreed-upon contract is honored.
Coupling has several dimensions. This vocabulary names possible dependencies; it is not a universal cost ranking:
- Message coupling: one module calls a method on another through a stable interface.
- Data coupling: modules share simple data through parameters and return values.
- Stamp coupling: modules exchange whole records when they only use a few fields.
- Control coupling: one module passes a flag that makes another branch.
- Content coupling: one module reaches into another's private state or implementation.
Inspect volatility, semantic promises, deployment boundaries and runtime costs. A tiny message API can still carry expensive temporal coupling or remote failures.
Why It Matters Here
You cannot build software that survives change without managing coupling. Every concept in this module exists to reduce unnecessary coupling without losing useful structure.
Specifically: the SOLID principles are largely about which coupling is safe. Depend on stable abstractions (DIP), depend on narrow interfaces (ISP), and extend rather than modify (OCP) -- all three are coupling-management moves.
Concrete Example
Tight coupling (content coupling, reaching into another class):
class ReportPrinter {
void print(Order order) {
// relies on publicly exposed representation
for (Item item : order.items) {
System.out.println(item.name + " " + item.price);
}
}
}
ReportPrinter now depends on Order.items being a public, iterable list of Item with name and price fields. Any change to the representation breaks the printer.
Loose coupling (message coupling through a narrow method):
class ReportPrinter {
void print(Order order) {
for (String line : order.lineItems()) {
System.out.println(line);
}
}
}
Order now owns the representation and exposes only a narrow operation. ReportPrinter knows less, so less can break.
Common Confusion / Misconception
"Less coupling is always better." It is not. Zero coupling is zero collaboration, and you are not building a system of lonely classes. The goal is to pay coupling only when you get something for it, and to compare dependency stability, information exposure, coordination cost and the needs of the caller. A stable concrete value can be a cheaper dependency than an unnecessary interface.
A second misconception: "two classes in the same file are coupled." File layout alone does not establish semantic coupling, although packaging, imports and deployment can add dependencies. Two classes in one file can be entirely independent; two classes in separate packages can be tightly coupled through shared mutable globals.
How To Use It
When writing or reviewing code, ask the coupling questions before reaching for a pattern:
- What does this module need to know about its collaborators?
- Is it reaching past an interface to touch state that should be private?
- If I changed a private field on the collaborator, would this code need to change too?
- Can the collaboration move to a stabler abstraction (interface, narrow method)?
Check Yourself
- Why can dependence on internal representation spread changes? When could a narrow remote call be more expensive?
- Why can two classes share data through parameters (data coupling) without being problematic?
- What is the difference between coupling and cohesion?
Mini Drill or Application
Pick any two modules you have written that depend on each other. Do all four:
- Name the coupling kind on that edge (data, stamp, control, content, message).
- Write the narrowest interface one could ask of the other.
- Identify one private detail currently leaking through the dependency.
- Sketch a rename or extraction that would pull the leak back behind the boundary.
Read This Only If Stuck
- Head First Design Patterns: The Power of Loose Coupling
- OOAD: Cohesion and coupling
- Design Patterns (GoF): Decoupling senders and receivers
Engineering extension: Temporal and semantic coupling
A narrow save(record) signature can still require an undocumented transaction, timezone or retry convention. Record valid call order, ownership and failures alongside data types. The proposed lineItems() strings also couple the domain to formatting; compare immutable line-item values plus an external renderer. Try: draw source-import arrows separately from runtime calls. Check: a remote one-method service may be harder to evolve than a local immutable data record.
Use the design contracts and practice feedback after an independent attempt.