Read and draw class diagrams

Boxes for classes, lines for relationships: association, aggregation, composition, inheritance and dependency.

Pattern: UML25 min read
Quick recall2 questions
  1. 01Which pillar lets a child class reuse its parent's code?

    Inheritance: the child extends the parent and gets its fields and methods.

  2. 02When does composition beat inheritance?

    When subclasses multiply for every mix of features. Hold small parts instead, and pick them when you build the object.

In a low-level design interview, the first thing you draw is a class diagram. It shows the classes and the lines between them, before anyone writes code.

This lesson uses one running example, a ride app like Uber or Ola. You learn one box and six kinds of line, then draw the whole app.

One class, one box

A class is a box with three parts, top to bottom: the name, the fields, and the methods.

The Trip class. Read the box top to bottom: name, fields, methods.
  • - marks a private member. Only the class itself can use it.
  • + marks a public member. Any class can call it.
  • # marks a protected member: the class and its children.
  • The type comes after a colon: fare: int, start(): void.

In an interview, show only the fields and methods that matter for the design. In a big diagram, a box with only the name is fine.

Six kinds of line

Each line answers one question: how do these two classes relate? Learn them one at a time, each with a pair from the ride app.

Association: knows

A rider books trips. The two classes know each other, but neither is a part of the other.

A plain line: Rider and Trip know each other. The word under it names the link.

Aggregation: has

A driver has a car. The car lives on its own: if the driver leaves the app, the car still exists and can join with another driver.

A hollow circle on the whole's side: Driver has a Car.

Composition: owns

A trip owns its stops: the pickup and the drop. The stops die with the trip. A stop from one trip means nothing in another.

A filled circle on the owner's side: Trip owns its Stops.

Inheritance: is a

Riders and drivers are both users. Each one is a User, and gets its name and phone from it.

A hollow triangle points to the parent. Rider and Driver extend User.

Interface implementation: promises

PaymentMethod is an with one method, pay(amount). CardPayment and UpiPayment each keep that promise in their own way.

A dashed line with a hollow triangle: both classes implement PaymentMethod.

Dependency: uses

At the end of a ride, Trip.end(method) asks a payment method to charge the fare. Trip uses it for one call and does not keep it.

An arrow with a filled head: Trip uses PaymentMethod, here as a parameter.

Many tools draw a dependency as a dashed arrow. This course keeps dashes for interfaces, so a solid ──▶ means "uses".

What the lines mean in code

The circles are the easiest to mix up. In code, the difference is who creates the part.

class Driver {
    private Car car;                  // aggregation: handed in

    Driver(Car car) {
        this.car = car;
    }
}

class Trip {
    private final Rider rider;        // association: a link
    private final List<Stop> stops;   // composition: made here

    Trip(Rider rider, String pickup, String drop) {
        this.rider = rider;
        this.stops = List.of(new Stop(pickup), new Stop(drop));
    }

    void end(PaymentMethod method, int fare) { // dependency: a parameter
        method.pay(fare);
    }
}
  • Aggregation: the Car exists first and is passed in. If the driver object goes away, the car object is still there.
  • Composition: Trip creates its own stops in the constructor. Nothing outside holds them, so they go away with the trip.
  • Association: Trip keeps a link to a Rider that lives on its own.
  • Dependency: PaymentMethod is only a parameter. Trip has no field for it.

Multiplicity: how many

A number at each end of a line says how many objects take part.

Read each end from the other side: one rider has zero or more trips.
  • 1: exactly one. Each trip has one rider.
  • 0..*: zero or more. A new rider has no trips yet.
  • 1..*: one or more. A driver must register at least one car.
  • Any range works: 2..* means two or more, like the stops of a trip.

In code, 1 is usually a constructor argument the object cannot exist without. 0..* is a list that may be empty.

The whole ride app

Put every line together, and the design of the app fits on one screen.

The ride app. Start at User, read down to Trip, then follow the lines out of Trip.
  • Rider and Driver extend User.
  • A driver has one or more cars. The cars live on their own.
  • Each trip knows one rider and one driver. Each of them has zero or more trips.
  • A trip owns two or more stops, and uses a PaymentMethod when it ends.
  • CardPayment and UpiPayment implement PaymentMethod. A new wallet is one new box under it.

Pick the right line

When you are not sure which line to draw, ask the test question.

RelationshipSymbolTest questionRide app
Association──Do they only know each other?Rider, Trip
Aggregation○──Does the part live on alone?Driver, Car
Composition●──Does the part die with the whole?Trip, Stop
Inheritance──▷Is it a kind of the parent?Rider, User
Implementation╌╌▷Does it keep an interface's promise?CardPayment, PaymentMethod
Dependency──▶Is it used only inside one call?Trip, PaymentMethod

If two lines both fit, pick the weaker one: association before aggregation, aggregation before composition. A stronger line is a stronger claim, and you must defend it.

Draw one in an interview

Start from the requirement, and leave the lines for last.

A rider books a trip from a pickup to a drop. A driver with a car accepts it. At the end, the rider pays by card or UPI.

  1. Nouns become boxes. Rider, trip, pickup and drop (one Stop class), driver, car, card and UPI.
  2. Verbs become methods. "books" is Rider.book(), "accepts" is Driver.accept(trip), "pays" is PaymentMethod.pay(amount).
  3. Add only the key fields. The fare and status of a trip, the plate of a car.
  4. Draw the relationships last. For each pair, ask the test questions from the table.
  5. Add multiplicity where the number changes the design, such as one car or many.
  6. Say what you would build next, such as UpiPayment, so the interviewer sees the design can grow.

Talk while you draw. The interviewer grades your reasons as much as your boxes.

Remember4 points
  1. 01

    A class box has three parts: the name, the fields and the methods. - is private, + is public.

  2. 02

    Aggregation (○──) receives a part that lives on its own. Composition (●──) creates the part, and the part dies with the whole.

  3. 03

    Inheritance (──▷) means "is a". Implementation (╌╌▷) keeps an interface's promise. Dependency (──▶) uses a class inside one call.

  4. 04

    In an interview: nouns to boxes, verbs to methods, relationships last, and multiplicity where it matters.

Read more

Next: the single responsibility principle, the first of the five SOLID rules.