Class Diagram
A class diagram is a contract you can compile: each box is a type, each connecting line is a reference one object holds to another, and the small adornments on that line (arrowheads, diamonds, numbers) encode exactly how that reference behaves at runtime — who can navigate to whom, how many objects sit on each end, and whose destruction takes the other down with it. Read the line, and you know what the constructor takes, what the field looks like, and what delete does.
The anatomy of one class box
A class is drawn as a rectangle split into three stacked compartments: name (top), attributes (middle), operations (bottom). Visibility prefixes map straight to access modifiers: + public, - private, # protected, ~ package. An italic class name or operation means abstract. Underlined members are static. This is the part that maps one-to-one onto source code.
The five relationship lines and what each one means
The whole expressive power of a class diagram lives in the line connecting two boxes. Each variant is a different statement about object references and lifetimes:
| Relationship | Notation | Meaning (the mechanism) | Code consequence |
|---|---|---|---|
| Association | plain line | One object holds a long-lived reference to another | A field of the other type |
| Aggregation | hollow ◇ diamond at the whole | "Has-a", but the part outlives the whole — shared, not owned | Field set from outside (injected); part is not destroyed with whole |
| Composition | filled ◆ diamond at the whole | "Owns-a" — the part's lifetime is bound to the whole's | Whole creates the part in its constructor; part dies with it |
| Generalization | hollow ▷ triangle at the parent | "Is-a" — subclass substitutes for superclass | extends / implements |
| Dependency | dashed arrow ⤍ | Uses transiently — a parameter, return, or local, no stored field | Mentioned in a method signature/body, not a field |
The single most-tested distinction here is aggregation vs composition, and it is decided by exactly one question: if I destroy the whole, does the part have any reason to keep existing?
Worked example: an airline reservation model
Take four real classes and read every line as a lifetime decision.
| Line | From → To | Notation | Why this and not something else |
|---|---|---|---|
| 1 | Flight ◆— WeeklySchedule | composition | A WeeklySchedule ("departs Mon/Wed/Fri") is meaningless once its Flight is deleted — it exists only as part of that flight. Flight creates it; deleting the flight deletes the schedule. |
| 2 | Airline ◇— Aircraft | aggregation | An Aircraft (tail number N7203U) keeps existing if the Airline folds — it gets leased, sold, re-registered. The airline references aircraft it doesn't own outright; lifetimes are independent. |
| 3 | FlightInstance "2" — "0..*" Pilot | association + multiplicity | One concrete flight on a date is crewed by exactly 2 pilots; one pilot flies many instances over a career. Both ends store references — bidirectional. |
| 4 | FlightReservation ⤍ Payment | dependency (dashed) | FlightReservation.collect(Payment p) uses a Payment passed in for one call. It does not keep a field — so it is a dashed dependency, not an association. |
Now trace what delete does, the way the runtime would:
- Delete FlightInstance UA90/2026-07-04. Its two Pilot references are associations, so the pilots survive — they just lose one entry from their own flight list. Nothing cascades to people.
- Delete its parent Flight UA90. The
WeeklySchedulehanging off it by composition is destroyed in the same breath — no other object could legally hold it. - Shut down the Airline. Each
Aircraft, attached by aggregation, keeps existing. They are reassigned to a new operator. The diagram predicted this: hollow diamond = no cascade.
That cascade-or-not behaviour is the payload of the diagram. Reading the diamond fill tells you, before you write a line of code, whether the whole's destructor must dispose the part.
When to use which relationship — and the trade-offs
The line you draw is a design commitment with real cost. A senior engineer picks deliberately:
Composition vs Aggregation
Choose composition when the part has no identity or use outside its whole and you want the whole to guarantee the part's invariants (it created it, so nothing else can hand it a broken one). You gain encapsulation and a clean lifecycle: one delete, no dangling parts. You pay with rigidity — the part can't be shared or swapped, and it's harder to unit-test the part in isolation because you can't inject a fake (the whole builds the real one internally).
Prefer aggregation when the part is shared across wholes or supplied from outside (an Aircraft serving several routes; a ConnectionPool handed to many services). You gain reuse and testability (inject a mock). You pay with the lifetime question becoming your problem: nobody auto-cleans the part, so you risk leaks or use-after-free-style bugs (a whole holding a reference to a part another owner already disposed).
One-liner: choose composition when destroying the whole should destroy the part; prefer aggregation when the part is shared or has a life of its own.
Association vs Dependency
Use an association (a stored field) when the collaborator is needed across many calls and across time. Prefer a dependency (a method parameter) when the collaborator is needed for one operation only. Promoting a dependency to an association costs you coupling and state to manage; demoting an association to a dependency costs you re-passing the object on every call. Default to the narrowest one that works — a dashed arrow is cheaper to change than a field.
Concrete pick
Modeling Order and its LineItems: line items have no meaning without their order and should vanish with it → composition. Modeling Order and the Customer who placed it: the customer clearly outlives the order and is shared across many orders → aggregation/association, never composition.
Pitfalls
- Drawing composition where aggregation is true. Modeling
University ◆— Studentimplies deleting the university destroys the students. Students transfer; they outlive the institution. This is the most common diamond error — apply the destruction test every time. - Confusing the diamond end. The diamond always sits on the whole, never the part. A diamond on the wrong side inverts the ownership and misleads everyone reading it.
- Using association for what is really a dependency. If
Paymentonly appears as a method argument, a solid line falsely impliesFlightReservationstores a payment forever. Reviewers will look for a field that isn't there. - Mis-stating multiplicity defaults. An unlabeled end is ambiguous, not "1". Always write the numbers. And remember directionality: an arrowhead means navigation is one-way — the unarrowed class doesn't hold a back-reference, which changes what queries are even possible.
- Over-modeling getters/setters and trivial classes. A class diagram is a communication tool. Cramming every accessor and every DTO makes the structural story invisible. Show the relationships that carry design risk.
- Treating the diagram as the source of truth after code drifts. Because class diagrams map directly to code, a stale one is actively misleading — worse than none. Regenerate from code or delete it.
- Assuming a composition diamond means the database cascade-deletes. UML composition is a statement about object lifetimes in memory, not about your persistence layer. An ORM will not delete the part rows for you unless you configure
ON DELETE CASCADE/orphanRemoval=true; a filled diamond in the diagram and a missing cascade rule in the schema is a real source of orphaned rows. The diagram states the intent — you still have to enforce it in the DB.
Takeaways
- Every line is a statement about references and lifetimes; learn to read the adornments and you read the code.
- Aggregation vs composition is decided by one test: does destroying the whole destroy the part? Filled diamond = yes (owns), hollow = no (shares).
- Default to the narrowest relationship — dependency over association, aggregation over composition — and widen only when the design demands it; each widening adds coupling or lifecycle burden.
- The diagram earns its keep only while it matches the code; a stale class diagram misleads precisely because the mapping is so direct.
Re-authored and deepened for this guide. Drawn from the OMG UML 2.5.1 specification (association, aggregation, and composition semantics), Martin Fowler's UML Distilled (the lifetime test for composition and the "diagram as communication" stance), Grady Booch et al., The Unified Modeling Language User Guide, and the Grokking the Object-Oriented Design Interview airline-system example, which supplied the Flight/WeeklySchedule and Airline/Aircraft cases. Placeholder figures replaced with hand-authored, labeled SVG diagrams; worked deletion trace and selection/trade-off guidance added.
🤖 Don't fully get this? Learn it with Claude
Stuck on Class Diagram? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Class Diagram** (OO & Low-Level Design) and want to truly understand it. Explain Class Diagram from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Class Diagram** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Class Diagram** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Class Diagram** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.