Class Diagram Relationships Explained
The lines in a class diagram carry as much meaning as the boxes. This guide explains every relationship type, the notation for each, and simple tests for choosing the right one.
Most confusion about UML class diagrams is not about the boxes; it is about the lines. Association, aggregation, composition, inheritance, realization, and dependency each have a distinct notation and, more importantly, a distinct meaning, and choosing the wrong one quietly misrepresents your design. A filled diamond where you meant a plain line is not a cosmetic difference; it makes a claim about object lifecycles that a careful reader will take seriously.
This guide walks through each relationship, gives the notation, and offers a simple test to distinguish the ones people mix up. Think of them as a spectrum from loosest coupling to tightest: dependency and association at the loose end, aggregation and composition in the middle, inheritance and realization expressing the strongest structural bonds. You can practice drawing all of these at /diagrams, and the broader class diagram walkthrough at /diagram-tools/uml-diagram provides more examples.
Association and dependency: the loose end
An association is the most basic relationship: a solid line meaning two classes are connected, typically because one holds a reference to the other over some meaningful period. An Order is associated with a Customer because it keeps a reference to the customer who placed it. Associations can carry multiplicity (how many of each) and role names (what each end is called from the other's perspective), and they can be directed with an arrowhead to show which class knows about the other.
A dependency is looser still, drawn as a dashed line with an open arrowhead. It means one class uses another transiently, without holding a lasting reference, most often because it takes the other as a method parameter, returns it, or references it locally. If OrderService.process(Order order) simply receives an Order to work on and does not store it, that is a dependency, not an association. The rule of thumb: association is a structural, has-a-reference relationship; dependency is a fleeting uses-it relationship.
Aggregation and composition: the has-a middle
Aggregation and composition are both whole-part relationships, and they are the pair people most often get wrong. Aggregation, drawn with a hollow diamond at the whole's end, means the whole contains parts that can exist independently of it. A Department aggregates Employees, but if the department is dissolved, the employees still exist; they can move to another department. The diamond points at the whole, the container.
Composition, drawn with a filled diamond, is a stronger whole-part relationship: the parts cannot meaningfully exist without the whole, and are typically created and destroyed with it. A House is composed of Rooms; delete the house and the rooms go with it, because a room has no independent existence. An Invoice is composed of LineItems. The test is lifecycle ownership: if destroying the whole should destroy the parts, use composition; if the parts outlive the whole, use aggregation. When you are genuinely unsure, prefer a plain association, because both diamonds make strong claims that are easy to get wrong.
Inheritance and realization: the structural bonds
Inheritance, formally generalization, is drawn as a solid line with a hollow triangular arrowhead pointing at the parent class. It expresses an is-a relationship: a SavingsAccount is-a Account, a Manager is-a Employee. The child inherits the parent's attributes and operations and may add or override them. Inheritance is the strongest coupling in a class diagram, which is exactly why it should be used deliberately rather than reflexively; an is-a claim that is not truly always true will haunt the design.
Realization, drawn as a dashed line with the same hollow triangular arrowhead, expresses that a class implements an interface: it promises to provide the operations the interface declares. A CreditCardProcessor realizes the PaymentProcessor interface. The distinction from inheritance is that realization is about fulfilling a contract of behavior rather than inheriting structure and implementation. In practice you use realization wherever your language has interfaces or protocols, and it is the backbone of designing to abstractions rather than concretions.
A quick reference for choosing
When you are staring at two classes and cannot decide which line to draw, run through these in order. Each has a distinguishing question, and the first one that fits is usually your answer.
- Is it an is-a relationship (a subtype)? Use inheritance, the hollow triangle.
- Does one class implement an interface's contract? Use realization, the dashed hollow triangle.
- Is it a whole-part where the part dies with the whole? Use composition, the filled diamond.
- Is it a whole-part where the part can exist independently? Use aggregation, the hollow diamond.
- Does one class hold a lasting reference to another? Use association, a plain solid line.
- Does one class merely use another as a parameter or local, without storing it? Use dependency, the dashed open arrow.
- Still unsure between aggregation and composition? Default to a plain association; it makes the fewest claims.