The Problem It Names
Objects in a real domain must reference each other — an Order needs its Customer. The design questions are subtler than “should there be a field”: which side holds the reference? how many on each end? and does knowledge flow one way or both? Association is the umbrella term for these structural “knows-a” links — the weakest and most common relationship type.
Direction
UNI-DIRECTIONAL BI-DIRECTIONAL
┌───────┐ order.customer ┌──────────┐ ┌───────┐ orders customer
│ Order │ ────────────────► │ Customer │ │ Order │◄────────►│ refs
└───────┘ └──────────┘ └───────┘◄────────►└────────┘
Customer never points back; Each can navigate to the other;
Customer code works with no both fields must be kept in sync
Order existing at all. by every mutating call site.
Left: only Order knows Customer. Navigation is one-way; Customer compiles without importing Order at all. Right: both hold fields, enabling customer.getOrders() — purchased at the price of maintaining two structures that must always agree. A forgotten sync (order.setCustomer(x) without adding to x.orders) silently corrupts navigation from the other side.
Java Mechanics
class Order { // uni-directional
private final Customer customer;
Order(Customer c) { this.customer = c; }
}
// bi-directional requires paired updates:
class Customer {
private final List<Order> orders = new ArrayList<>();
void addOrder(Order o) { orders.add(o); }
}
class Order {
private Customer customer;
void attachTo(Customer c) {
if (this.customer != null) this.customer.removeOrder(this);
this.customer = c;
c.addOrder(this); // both sides updated together
}
}
Multiplicity
UML annotates each end: 1, 0..1, *, 1..* — “an Order has exactly 1 Customer; a Customer has 0..* Orders.” Zero-capable multiplicities drive null-handling and empty-collection decisions in code; interviewers probe them deliberately (“can a user have zero orders?” decides whether getOrders() may return empty vs throw).
Trade-offs
| Uni-directional | Bi-directional | |
|---|---|---|
| Navigation | One way only | Both ways |
| Sync cost | None | Every mutation touches two sides |
| Coupling | Lower (one import direction) | Higher (circular imports likely) |
| Memory retention | Referenced object kept alive by holder | Both retained while either lives |
| Serialization/GC hazards | Minimal | Cycles need special handling |
Real-World Usage
- Default to uni-directional; add the reverse pointer when a proven query need exists.
- ORMs formalize this: JPA
@ManyToOne(owning side) +@OneToMany(mappedBy=...)— the annotation exists precisely because bidirectional sync is error-prone. - Bidirectional memory retention matters for caches: a cache entry holding a back-reference to its owner prevents collection of both.
Interview Signals
- Stating direction explicitly on the diagram (“orders know customers, not vice versa”) reads as deliberate design.
- The experienced answer to “why not always bidirectional?”: sync burden + coupling + retention — three concrete costs, not taste.
Premium Content
Unlock Association and all premium lessons with a subscription.
From ₹199.99/year — See plans