Aggregation and Dependency
The weaker "has-a" and "uses-a" relationships — borrowed parts and temporary collaborators
Not every relationship between two objects is the strong ownership we saw with composition. Often one object simply uses another, or holds a reference to another that exists independently. UML and the OOP tradition give these weaker relationships names: aggregation and dependency.
Knowing the difference is not pedantry — it changes how you draw your model, which object's constructor takes which, and what happens when one object goes away.
A spectrum of relationships
| Relationship | Lifetime | UML arrow | Java shape |
|---|---|---|---|
| Dependency | The collaborator is borrowed for one method call | dashed arrow ..> | Method parameter or local |
| Aggregation | The whole holds a reference, but the part lives independently | open diamond o-- | Field, but constructed outside and passed in |
| Composition | The whole owns the part; both live and die together | filled diamond *-- | Field, usually constructed inside the whole |
Aggregation: a Library has Members (who exist on their own)
A Library keeps a list of its Members. But each Member is a
person who exists in the world whether or not the library does. If
the library closed tomorrow, the members would not vanish — they
would just stop being members. That is aggregation.
Two signals in the code reveal that this is aggregation rather than composition:
Librarydid not callnew Member(...). It received the members from the outside.- The same
Memberobject could be registered with two different libraries simultaneously — the membership relationship is not exclusive.
Composition vs. aggregation, side by side
To make the difference vivid, here are two models of a House. In
one, the Rooms are part of the house (composition). In the other,
the Tenants are people who happen to live in the house this year
(aggregation).
You can usually decide which one applies by asking: "If the whole disappears, does the part disappear too?" If yes → composition. If the part keeps existing → aggregation.
Dependency: a method that uses another object briefly
A dependency is the briefest relationship of all. One class doesn't hold a reference to another, but does briefly touch it to do its job. In the code, this usually looks like a method parameter or a local variable.
OrderPrinter has no fields. It needs an Order and a Printer
only for the duration of print. That is a dependency. It's the
loosest possible coupling: as soon as print returns, the two
references go away.
Why these distinctions matter in practice
The category you choose changes who is responsible for what:
| Question | Composition | Aggregation | Dependency |
|---|---|---|---|
| Who creates the part? | The whole | Someone else | Someone else (per call) |
| Where is the reference held? | A field in the whole | A field in the whole | A parameter / local |
| What if the part is shared? | It shouldn't be | Fine | Fine |
| Lifetime of the part? | Tied to the whole | Independent | Independent |
| Mental model | "made of" | "knows about" | "uses for a moment" |
In a real design, the same class often participates in all three
kinds of relationships with different collaborators. A Library is
composed of its Catalog, aggregates Members, and may depend
on a Clock passed in just to stamp due dates.
Passing dependencies in: a tiny taste of injection
The healthy pattern is to pass collaborators in (via constructor or method parameter) rather than to construct them inside. This is sometimes called dependency injection when done systematically, but the simple version is just good Java.
Because the logger is handed in, the same Greeter can be wired
up with a different logger (a file logger, a silent logger, a
test-spy logger) without changing a single line of Greeter's code.
That is the deepest benefit of aggregation and dependency: they keep
your classes loosely coupled and easy to recombine.
Practice
Implement two classes:
Studentwith a private finalnameand aname()accessor.Classroomwith:- a
private final List<Student> students = new ArrayList<>(); enroll(Student s)adds the student.size()returns the number enrolled.names()returns the enrolled students' names joined by", ". If the classroom is empty, return the empty string"".
The students are created in Main and handed in — Classroom
must not call new Student(...).
Expected output:
size=0
size=3
names=Ada, Bob, Cleo
Test your understanding
A Library has a list of Member objects. Each Member exists as a person regardless of the library. Which relationship is this?
Composition
Aggregation
Dependency
Inheritance
A method OrderPrinter.print(Order o, Printer p) takes its collaborators as parameters and doesn't hold them as fields. What kind of relationship is this?
Composition
Aggregation
Dependency (uses-a)
Inheritance
Why is "passing collaborators in" (rather than constructing them inside the class) usually a better design?
It runs faster because there are fewer new calls
It saves typing
It keeps the class loosely coupled — the same class can work with different implementations or test stand-ins
It is required for the class to compile