Class diagram
What are the pieces, and how are they wired together? The class diagram is UML's
workhorse - the one notation every other diagram on this site borrows boxes from, and
the one you will draw the most in an actual LLD interview. Everything below builds up a
LoginForm / AuthService / Database login flow in stages, so by the end you have
seen every piece of notation land on the same running example.
The three compartments
A class box is always the same three stacked rectangles: the name on top, its attributes (state) in the middle, its operations (behavior) at the bottom. Nothing else belongs in the box - no prose, no comments about what the class does.
A class with no attributes or operations worth showing can drop to just the name compartment - useful when a box exists only to be pointed at by a relationship, not to be read in detail.
Visibility markers
Every attribute and operation is prefixed with a symbol that says who else is allowed to touch it:
| Symbol | Visibility | Who can see it |
|---|---|---|
+ | Public | Anything, anywhere |
- | Private | Only this class |
# | Protected | This class and its subclasses |
~ | Package / internal | Anything in the same package or module |
AuthService above already uses two of them: + login(...) is the public entry point
callers are meant to use, - hash(...) is a private helper nobody outside the class
should call directly. Reach for # the moment a subclass genuinely needs to override or
call a member its parent defines, and ~ only if your language actually has a
package-private concept to draw - plenty of UML diagrams for Python or JavaScript code
skip it entirely.
Attribute and method signature syntax
Two fixed shapes, borrowed straight from statically typed languages, used here even when the code itself is dynamically typed:
- Attribute:
name: Type-- secretKey: string,# balance: double. - Method:
name(param: Type, ...): ReturnType-+ login(user: String, pass: String): boolean. A method with no meaningful return value omits the: ReturnTypeentirely rather than writing: void.
The type names are illustrative, not a commitment to a language. A diagram meant for a
Python codebase can write - secretKey: str and nobody will object; the notation cares
about the shape of the signature, not which language's spelling of String you used.
Static and abstract rendering
Two rendering conventions layer on top of the same member list, and they answer two different questions - "does this belong to the class or to each object?" and "does this have a body at all?":
- Underlined - the member is static: it belongs to the class itself, shared across every instance, not to any one object.
- Italic - the member (or the whole class) is abstract: declared here with no body, filled in by a subclass.
maxSessions is one shared cap for every Session object, hence underlined. renew()
has no implementation on Session itself, hence italic - and the class name is italic
too, marking the whole class abstract, which forces every concrete subclass to supply
its own renew().
Interface, abstract class and enum stereotypes
Three special class kinds get a «stereotype» label above the name, in addition to (or
instead of) the italics rule above:
An interface never has fields and never finishes a method body - every member is
implicitly abstract, which is why the stereotype alone is enough without italicizing
each line. An abstract class can mix both, exactly like Shape above. An enum lists its
named constants directly in the member compartment instead of attributes.
Relationship lines and arrowheads
Every line between two boxes is one of a fixed small set, and the only thing that tells them apart is the line style and the arrowhead - the notation carries no color, no thickness variation, nothing else:
The full reasoning for choosing between association, dependency, aggregation and composition - and the fifth relationship, realization, on its own - lives in Class relationships; this page only needs you to recognize the line on sight.
Multiplicity notation
A number (or range) written at each end of an association line says how many objects on that end can be linked to one object on the other end, at any moment. It is written right next to the class it constrains, not in the middle of the line:
The common values, and what each one promises:
| Notation | Meaning |
|---|---|
1 | Exactly one |
0..1 | Zero or one (optional, never more than one) |
* (or 0..*) | Zero or more |
1..* | One or more (at least one) |
2..5 | Any exact range |
Order "1" -- "1..*" LineItem above reads as "one Order has one or more LineItems, and
every LineItem belongs to exactly one Order" - which also tells you, without a word of
prose, that a LineItem cannot exist floating on its own.
The login flow, built up in stages
Same technique, assembled on the running example one decision at a time - this is the order you would actually draw it in, live, in an interview.
Stage 1 - just the nouns. Get the classes on the page before committing to what is inside any of them.
Stage 2 - wire the relationships. LoginForm uses an AuthService to do its work;
AuthService uses a Database to look users up. Neither holds the other as a permanent
field yet, so these are dependencies for now.
Stage 3 - add the operations. The verbs from the prompt - submit credentials, log a user in, find a user record - become methods on whichever class owns the data they touch.
Stage 4 - add the state, and promote a dependency to an association. AuthService
turns out to need Database on every call, not just once, so it becomes a held field -
an association, drawn with a plain solid line instead of dependency's dashed one - and
gets a multiplicity: one AuthService uses exactly one Database.
Four stages, and the diagram now says something a paragraph of prose would have taken three times as long to say precisely: what exists, how long each link lasts, and who is responsible for which verb.
When to reach for it: any time you are about to write code with more than one class in it - it is the default, load-bearing diagram of the bunch. When not to bother: for a single class with no interesting relationships, just write the class; a box with one name in it is not a diagram.
Common mistakes
- Drawing every getter and setter. A class box crowded with a dozen accessor pairs hides the two or three methods that actually matter. Show the behavior worth talking about; trust that accessors exist.
- Confusing which end owns the relationship. An arrow (or an association line with
multiplicities) has a direction.
Order --> CustomerandCustomer --> Orderare different claims about who holds a reference to whom - getting this backwards silently describes a different design than the one you meant. - Forgetting multiplicity when it is the whole point. "A
TeamhasPlayers" is incomplete - can a team have zero players mid-season? Is there a cap? The multiplicity is often the one piece of information an interviewer is actually listening for. - Reaching for inheritance to get code reuse, with no real "is-a" relationship. Two classes sharing a field and a method by coincidence are not a hierarchy; see Favor composition over inheritance before drawing the arrow.
Try it yourself: sketch a class diagram for a Book, a Library, and a Member,
building it up the way Stage 1 through 4 did above - nouns first, then relationships,
then operations, then multiplicities. Does a Library "have" Books by composition or
aggregation? What multiplicity goes on each end?
Check yourself
A member is written `# balance: double`. What does the `#` mean, and who can see this field?