Open any food delivery app and count the objects that have to talk to each other: a customer places an order, the order comes from a restaurant, a rider picks it up, a payment is made for it. None of these objects owns the others. A restaurant does not disappear when one order is cancelled, and a rider is still a rider after the trip ends. They are simply connected for a while so they can do their work. That connection is called an association.

Association = a relationship in which one object holds a reference to another so it can use it or talk to it, while both objects keep their own, independent lives.

Real-world analogy: A patient and their family doctor. The patient knows which doctor to visit, and the doctor has a list of patients. Neither one owns the other: the patient can switch doctors, and the doctor keeps practising if a patient moves away. The relationship is real and useful, but both people exist on their own.

1. What Is Association?

Class A is associated with class B when an A object keeps a reference to a B object (in a field or a list) because it needs it to do its job. Key points:

  • It is a "knows-a" or "uses-a" relationship. A FoodOrder knows its Restaurant. A Trip knows its Driver.
  • No ownership. The associated object is usually created somewhere else and passed in.
  • Independent lifecycles. Destroying one object does not destroy the other.
  • It has a direction and a multiplicity: who knows about whom, and how many objects are on each side.

Association is the most general relationship between classes. Aggregation and composition are special, stronger kinds of association that add a "whole and its parts" meaning:

RelationshipMeaningLifecycleExample
AssociationA knows or uses BIndependentFoodOrder and Restaurant
AggregationA is a whole that groups B partsParts live on their ownBus depot and its buses
CompositionA owns B partsParts are created and destroyed with AOrder and its order lines

Do not confuse association with dependency. If a class only receives another object as a method parameter, uses it, and forgets it (for example invoice.print(printer)), that is a dependency. Association means the reference is kept in a field.

2. UML Notation

In a UML class diagram, association is drawn as a solid line between the two classes.

  • An arrowhead shows direction: FoodOrder --> Restaurant means the order knows the restaurant, not the other way round. A plain line with no arrowheads means both sides know each other.
  • Multiplicity is written at each end: 1 (exactly one), 0..1 (optional), * (any number), 1..* (at least one).
  • A label on the line names the relationship, such as "from", "mentors" or "drives".

For comparison: a hollow diamond at the "whole" end means aggregation, a filled diamond means composition, and a hollow triangle means inheritance. Plain association has no diamond.

3. Unidirectional Association

In a unidirectional association only one class holds a reference. The other class does not know it is being referred to. This is the simplest and most common form, and it should be your default.

Output

Order #1: Chicken Biryani from Paradise Biryani (Secunderabad)
Order #2: Double Ka Meetha from Paradise Biryani (Secunderabad)
Same restaurant object: true
Order #1 cancelled. Restaurant still exists: Paradise Biryani
  • FoodOrder receives the Restaurant in its constructor and keeps it. It never creates or deletes the restaurant.
  • Both orders point to the same restaurant object. There is one Paradise Biryani in the system, not a copy inside every order.
  • Cancelling and discarding order #1 has no effect on the restaurant. Their lifecycles are independent, which is exactly what association means.
  • In C++ the link is a plain (non-owning) pointer, and the restaurant must outlive the orders that point to it. That rule is part of the design.

Why not also give Restaurant a list of all its orders? Because nothing needs it yet. Every extra reference has to be kept up to date, and in a real system the list of orders would come from a database query, not from a field.

4. Bidirectional Association

Sometimes both sides really do need to reach each other. In a company's internship programme, a mentor needs the list of their interns, and each intern needs to know their mentor. That is a bidirectional association.

The hard part is keeping both sides in agreement. If you add an intern to Arjun's list but forget to update the intern's own mentor field, or forget to remove them from Kavya's list, the data contradicts itself. The fix is to change the link in exactly one method that updates both sides:

Output

Kavya mentors: Sneha, Rahul
Sneha's mentor: Kavya
-- Sneha moves to Arjun --
Kavya mentors: Rahul
Arjun mentors: Sneha
Sneha's mentor: Arjun
  • addIntern() is the only way to change the relationship. It removes the intern from the old mentor, adds them to the new list, and updates the back-reference, all in one place.
  • The guard at the top (if already in the list, return) makes the method safe to call twice.
  • Callers outside the two classes cannot set intern.mentor directly. In Java the setter is package-private and named "internal". In C++ the field is private and only Mentor is a friend. In Python and JavaScript the convention is the same: only Mentor touches it.

Use bidirectional associations only when both directions are genuinely needed. They are more code and more chances for bugs.

5. Multiplicity: One-to-One, One-to-Many, Many-to-Many

Multiplicity says how many objects take part on each side:

  • One-to-one: one driver is assigned one cab at a time. A single field on each side that needs it.
  • One-to-many: one restaurant receives many orders, one mentor has many interns. A list on the "one" side, a single field on the "many" side, or both.
  • Many-to-many: a reader reviews many books, and a book is reviewed by many readers.

Many-to-many links often carry their own data. A review is not just "Anu is linked to Wings of Fire": it also has a star rating. When the link has data, turn the link itself into a class. This is called an association class, and it works just like a join table in a database.

Output

Wings of Fire: 2 reviews, average 4.5
The Alchemist: 1 review, average 3.0
Anu reviewed: Wings of Fire (5), The Alchemist (3)
Kiran reviewed: Wings of Fire (4)
  • Review connects one reader to one book and stores the stars. Each side keeps a list of Review objects, not a list of the other class.
  • The Review constructor adds itself to both lists, so the two sides can never disagree.
  • Questions from either side are now easy: a book's average rating, or every book a reader has reviewed.

6. Putting It Together: A Cab Booking System

Real designs mix several kinds of association. In a cab booking app:

  • Driver --> Cab is one-to-one and unidirectional: the driver knows the cab they drive.
  • Trip --> Rider is many-to-one and unidirectional: a trip knows its rider, but the rider class does not track trips.
  • Trip -- Driver is many-to-one and bidirectional: a trip knows its driver, and the driver keeps a list of trips to calculate earnings.
  • Trip itself works as an association class between rider and driver, carrying the distance and the fare.

Output

Trip 1: Priya with Ramesh in Swift Dzire (TS07 UB 4411), 8 km, Rs.146
Trip 2: Vikram with Ramesh in Swift Dzire (TS07 UB 4411), 15 km, Rs.230
Ramesh completed 2 trips, earned Rs.376

No class here creates or destroys another. Riders, drivers and cabs exist before any trip and stay after it. A trip just links them for one ride and remembers the details.

7. How to Choose the Right Association

  • Start unidirectional. Add the reverse direction only when a real use case needs it.
  • Prefer passing objects in (through the constructor or a setter) rather than creating them inside. That keeps the classes loosely coupled and easy to test.
  • Keep bidirectional links in sync inside one method, never from outside code.
  • If the link has data (a date, a rating, a price, a role), make it a class.

8. Common Mistakes

  • Making every association bidirectional "just in case". It doubles the work of keeping data consistent.
  • Updating only one side of a bidirectional link, which leaves the objects contradicting each other.
  • Creating the associated object inside the class (this.restaurant = new Restaurant(...)). That turns a loose association into ownership and makes the class hard to test.
  • Copying the associated object instead of referring to it, so there are two "Paradise Biryani" objects that drift apart.
  • Infinite loops when two sync methods call each other without a guard check.
  • Using many-to-many lists when the link has its own data. Introduce an association class instead.

9. Summary

ConceptKey point
AssociationAn object keeps a reference to another to use it; both live independently
UMLSolid line; arrowhead for direction; multiplicity at each end
UnidirectionalOnly one side knows the other. The best default
BidirectionalBoth sides know each other; update both sides in one method
MultiplicityOne-to-one, one-to-many, many-to-many
Association classTurn the link into a class when it carries data (Review, Trip)
vs Aggregation / CompositionThose are stronger forms that add a whole-part meaning and, for composition, ownership

Association is the glue of every LLD design. Get the direction and multiplicity right, keep two-way links consistent, and your classes will work together without owning more than they should.