System Design Road

LLD concepts · part 3 of 7

Class diagrams: the six lines that describe any design

In a low-level design interview you’ll almost always sketch a class diagram before writing code. It’s the fastest way to show your design and get feedback while changing it is still cheap. You don’t need all of UML. You need a small, precise vocabulary, and the habit of building the diagram up from the requirements instead of drawing it all at once. This chapter covers both, and shows exactly what each line on the diagram turns into in code.

9 min read6 diagrams8 sections

The short version

  • A class box has three parts: name, fields, methods. + is public, - is private, # is protected. Interfaces, abstract classes and enums are marked above the name.
  • Six relationships cover almost everything, and each has a clear shape in code: extends, implements, a field you own, a field you share, a field you refer to, or a parameter you use briefly.
  • Composition versus aggregation comes down to one question: if the whole disappears, does the part disappear with it?
  • Multiplicity (1, 0..1, , 1..) on a line says how many objects are at each end.
  • Class diagrams show structure; sequence diagrams show a flow over time. Interviews usually need one of each. Build both up in versions, from the requirements.

Before we start

A class diagram is a map of your design. Each box is a class, and each line says how two classes are connected. An interviewer reads it in seconds and immediately sees whether your design makes sense, long before any code exists.

UML (the Unified Modeling Language) defines many diagram types. For LLD interviews, two matter:

DiagramShowsDraw it when
Class diagramthe classes, what they hold, and how they’re connectedalways, after finding the entities
Sequence diagramone flow, call by call, over timeto walk through the main use cases

This chapter covers the class box, the six relationships (each with the Java it turns into), multiplicity, sequence diagrams, and a method for building a diagram from requirements. The LLD practice workspace has a whiteboard with exactly these boxes and arrows, so you can practise drawing them.

The class box

A class is a box with three sections: the name, the fields, and the methods.

ParkingSpot- id: String- size: SpotSize- occupant: Vehicle+ tryOccupy(v: Vehicle): boolean+ release(): void# log(msg: String): void

The rules:

MarkMeansJava
+publicpublic
-privateprivate
#protectedprotected
~package-privateno modifier
name: Typea field and its typeprivate Type name;
method(arg: Type): Resulta method, its parameters and return typeResult method(Type arg)

And the kinds of box, marked above the name:

«interface»PricingStrategy+ fee(t: Ticket, exit: Instant): long«abstract»Piece- color: Color+ moves(b, from): List«enum»SpotSizeSMALLMEDIUMLARGE

Interfaces have methods but no fields. Abstract classes are written in italics in UML (our diagrams mark them «abstract»). Enums list their values.

How much to write: in an interview, only what matters to the design. The key fields, the methods other classes call, and the types that show relationships. Skip getters, setters, constructors and toString. A box with fifteen trivial methods hides the three that matter.

The six relationships

Every connection between two classes is one of six kinds. They differ in how strongly the two are tied together, from “is a kind of” down to “uses for a moment”. Each one turns into a specific shape in code, and knowing that mapping is what lets you draw a diagram from code or code from a diagram.

RelationshipLineRead it asIn code
Inheritancesolid line, hollow triangle“is a”class B extends A
Realizationdashed line, hollow triangle“can do”, “implements”class B implements A
Compositionsolid line, filled diamond at the owner“is made of; parts die with it”a field the owner creates and never shares
Aggregationsolid line, hollow diamond at the owner“has; parts live on their own”a field holding things created elsewhere
Associationsolid line, open arrow“knows about”a field referring to another object
Dependencydashed line, open arrow“uses for a moment”a parameter, local variable or return type

Here are all six, on one small design for a food delivery app:

«interface»PaymentMethod+ charge(paise): booleanUpiPayment«abstract»Account- id: StringCustomerAccountOrder- lines: List- customer: CustomerAccountOrderLine- dish: String- qty: intRestaurant- menu: ListDish- name: StringInvoicePrinter+ print(o: Order): String1..*placed by0..*

The rest of this section takes each line in turn, with the code it means.

Inheritance: “is a”

A solid line with a hollow triangle pointing at the parent.

Java
abstract class Account {
    protected final String id;
    Account(String id) { this.id = id; }
}

final class CustomerAccount extends Account {               // the triangle
    CustomerAccount(String id) { super(id); }
}

Use it only for a genuine “is a” with shared behaviour; chapter 1 shows why. On a whiteboard, put parents above children so triangles point up.

Realization: “can do”

A dashed line with a hollow triangle pointing at the interface. It means the class implements the interface.

Java
interface PaymentMethod {
    boolean charge(long paise);
}

final class UpiPayment implements PaymentMethod {           // the dashed triangle
    public boolean charge(long paise) { return paise > 0; }
}

Realization lines are where your design’s extension points are. An interviewer looking for “how would you add X?” looks here first.

Composition: “made of, and can’t exist without”

A filled diamond at the owner’s end. The parts are created by the owner, belong to only that owner, and have no meaning without it. Delete the order and its lines go with it.

Java
final class Order {
    private final List lines = new ArrayList<>(); 1
    private final CustomerAccount customer;                     //     see "Association"

    Order(CustomerAccount customer) { this.customer = customer; }

    void add(String dish, int qty) { lines.add(new OrderLine(dish, qty)); }   // (2)
    List lines() { return List.copyOf(lines); }
}

record OrderLine(String dish, int qty) { }
  1. 1created inside
  1. The owner holds the parts privately.
  2. The owner creates them. Nobody else holds a reference to a line that could outlive the order.

Aggregation: “has, but they live separately”

A hollow diamond at the owner’s end. The owner holds a collection, but the parts were created elsewhere and can exist without it, or belong to several owners. A restaurant’s menu lists dishes, but a dish can be on several menus (dine-in and delivery), and still exists if one menu is deleted.

Java
record Dish(String name) { }

final class Restaurant {
    private final List menu = new ArrayList<>();
    void addToMenu(Dish d) { menu.add(d); }                     // (1)
    List menu() { return List.copyOf(menu); }
}
  1. The dish comes from outside, and may be shared. That’s the difference from composition in code.

The lifetime test

Composition and aggregation look almost the same, and confuse everyone. One question settles it:

Java
class LifetimeDemo {
    public static void main(String[] args) {
        Dish thali = new Dish("Veg thali");
        Restaurant dineIn = new Restaurant(), delivery = new Restaurant();
        dineIn.addToMenu(thali);
        delivery.addToMenu(thali);                               // shared by two owners
        dineIn = null;                                           // close dine-in
        System.out.println("delivery menu still has: " + delivery.menu());

        Order o = new Order(new CustomerAccount("c1"));
        o.add("Veg thali", 2);
        System.out.println("order lines: " + o.lines());
    }
}
Text
delivery menu still has: [Dish[name=Veg thali]]
order lines: [OrderLine[dish=Veg thali, qty=2]]

The thali outlived the dine-in menu: aggregation. An order line on its own (“2 × veg thali”, for no order, for nobody) is meaningless: composition.

WholePartPart outlives whole?Relationship
Parking floorParking spotnocomposition
OrderOrder linenocomposition
GameBoardnocomposition
TeamPlayeryesaggregation
RestaurantDish on its menuyes (shared)aggregation
Parking lotActive ticketsyes (the driver keeps it)aggregation

If you’re unsure in an interview, say which you chose and why. The reasoning earns more credit than the diamond.

Association: “knows about”

A solid line, usually with an open arrow showing which side knows about the other. One class keeps a reference to another for a long time, but doesn’t own it. The order above holds the customer who placed it: the customer exists before the order and long after it.

In code, it’s a field, like aggregation, but for a single related object rather than a collection the class “has”. The line can carry a label for the role (“placed by”) and multiplicity.

Dependency: “uses for a moment”

A dashed line with an open arrow. One class uses another briefly: as a parameter, a local variable or a return type, but doesn’t keep it.

Java
final class InvoicePrinter {
    String print(Order o) {                                     // (1)
        StringBuilder sb = new StringBuilder("INVOICE\n");
        for (OrderLine l : o.lines())
            sb.append(l.qty()).append(" x ").append(l.dish()).append('\n');
        return sb.toString();
    }
}
  1. The printer needs an order only while printing. If it stored the order in a field, it would be an association instead.

Association or dependency? Ask: is it a field? A field is an association (or composition or aggregation). A parameter or local variable is a dependency.

Multiplicity: how many?

Numbers at the ends of a line say how many objects take part.

WrittenMeansExample
1exactly onea ticket has exactly one spot
0..1none or onea spot has at most one vehicle
* or 0..*any number, including nonea restaurant has any number of dishes
1..*at least onean order has at least one line
2..4a rangea game of ludo has two to four players

Multiplicity carries real design information. “An order has 1..* lines” means an empty order is invalid, so something must stop one being created: that’s a check in the builder or constructor. “A spot has 0..1 vehicles” is the whole concurrency problem of the parking lot in three characters.

Sequence diagrams: one flow, over time

A class diagram shows structure: what exists and how it’s connected. It doesn’t show what happens. For that, draw a sequence diagram: the objects across the top, time going down, and each call as an arrow.

AppCheckoutPaymentMethodOrder1 pay(order, upi)2 total()3 ₹4504 charge(45000)5 true6 markPaid()7 receipt

The rules:

ElementMeans
Box at the topan object (or a role) taking part
Solid arrowa call
Dashed arrowa return value
Arrow to itselfa call to its own method

Draw one for each main use case after the class diagram’s first version. Walking a flow call by call is where you discover missing methods and wrong owners while changing them is still just redrawing a box. Every problem walkthrough in this track has a step called “walk the flows” for exactly this reason, and it changes the design every time.

From requirements to diagram

Don’t draw the final diagram in one go. Build it in versions, out loud, so the interviewer can follow and correct you early. Here’s the method, applied to a small library system: members borrow books; each book can have several copies; a member can hold at most five loans; late returns pay a fine that may change.

Step 1Nouns become candidate classes

Library, member, book, copy, loan, fine, due date. Keep the ones with their own data and rules (the parking lot walks through this filter in detail). “Due date” is a field on a loan; “fine” is a rule that will change, so it becomes an interface.

Step 2The structure

Library- books: List- members: ListBook- isbn: String- title: StringBookCopy- barcode: StringMember- id: String0..*1..*0..*

A book is made of its copies (a copy of nothing is meaningless): composition. The library has books and members that it didn’t create in any meaningful sense: aggregation.

Step 3The things that happen

A loan connects a member and a copy, with dates. It’s an object in its own right, because it has its own data (due date) and rules (one active loan per copy).

Library+ lend(m, copy): Loan+ giveBack(copy): longMember- id: StringLoan- due: LocalDate- returned: LocalDateBookCopy- barcode: Stringactiveborrowerof

Step 4What will change

The fine rule will change, so it goes behind an interface. That’s the realization line an interviewer looks for.

Library- fines: FinePolicy+ giveBack(copy): long«interface»FinePolicy+ fine(loan, today): longPerDayFineCappedFine

Step 5Check every requirement has a home

RequirementWhere it lives
Members borrow booksLibrary.lend creates a Loan
Several copies per bookBook *-- BookCopy
At most five loans per memberchecked in Library.lend, using active loans
Late returns pay a fineFinePolicy, applied in Library.giveBack
Fine rules changethe interface, with two implementations

That table is the last thing to show. If a requirement has no home, the design is incomplete; if a class serves no requirement, it shouldn’t be there.

Common mistakes

  • Drawing everything at once, so the interviewer can’t follow and you can’t change direction cheaply.
  • A class for every noun, including people and places the system never stores.
  • Aggregation everywhere. When unsure, a plain association line is fine. Wrong diamonds are worse than no diamonds.
  • Arrows pointing the wrong way. Inheritance and realization triangles point at the parent or interface; composition and aggregation diamonds sit at the owner.
  • No multiplicity on the lines that matter, especially 0..1 and 1..*, which carry rules.
  • Getters and setters in every box, hiding the methods that matter.
  • A class diagram with no sequence diagram, so nobody checks that the design actually supports the flows.

How this shows up in interviews

Most LLD rounds expect a class diagram within the first 15 to 20 minutes, built in versions and talked through. A good rhythm:

  1. Two minutes: boxes for the core entities, with the relationships that show structure.
  2. Five minutes: the key methods and the interfaces where change will come.
  3. Five minutes: a sequence diagram for the main flow, fixing the class diagram as the flow exposes gaps.
  4. Then code, starting from the smallest piece that must be correct.
Interviewer

Interviewer: Is the relationship between a parking floor and its spots composition or aggregation?

You

You: Composition. A spot has no meaning outside its floor: it’s created as part of the floor, it’s never moved to another floor, and if the floor is demolished the spots go with it. Compare the lot and its active tickets: a ticket is handed to the driver and outlives the lot’s list of active tickets, so that one is aggregation.