01Which pillar lets a child class reuse its parent's code?
Inheritance: the child extends the parent and gets its fields and methods.
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.
┌────────────────────────────────────────────┐ │ │ │ ┌──────────────────────┐ │ │ │ Trip │ name │ │ ├──────────────────────┤ │ │ │ - id: String │ fields │ │ │ - fare: int │ - private │ │ ├──────────────────────┤ │ │ │ + start(): void │ methods │ │ │ + end(method): void │ + public │ │ └──────────────────────┘ │ │ │ └────────────────────────────────────────────┘
-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.
┌────────────────────────────────────────────┐ │ │ │ ┌───────┐ ┌──────┐ │ │ │ Rider ├───────────┤ Trip │ │ │ └───────┘ books └──────┘ │ │ │ └────────────────────────────────────────────┘
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.
┌────────────────────────────────────────────┐ │ │ │ ┌────────┐ ┌─────┐ │ │ │ Driver │○──────────┤ 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.
┌────────────────────────────────────────────┐ │ │ │ ┌──────┐ ┌──────┐ │ │ │ Trip │●──────────┤ Stop │ │ │ └──────┘ └──────┘ │ │ │ └────────────────────────────────────────────┘
Inheritance: is a
Riders and drivers are both users. Each one is a User, and gets its name and phone from it.
┌────────────────────────────────────────────┐ │ │ │ ┌───────┐ │ │ │ Rider ├───┐ │ │ └───────┘ │ ┌──────┐ │ │ ├───▷│ User │ │ │ ┌────────┐ │ └──────┘ │ │ │ Driver ├──┘ │ │ └────────┘ │ │ │ └────────────────────────────────────────────┘
Interface implementation: promises
PaymentMethod is an with one method, pay(amount). CardPayment and UpiPayment each keep that promise in their own way.
┌──────────────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ CardPayment ├╌╌╌┐ ┌───────────────┐ │ │ └─────────────┘ ╎ │ <<interface>> │ │ │ ├╌╌╌▷│ PaymentMethod │ │ │ ┌────────────┐ ╎ └───────────────┘ │ │ │ UpiPayment ├╌╌╌╌┘ │ │ └────────────┘ │ │ │ └──────────────────────────────────────────────┘
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.
┌─────────────────────────────────────────────┐ │ │ │ ┌──────┐ end(method) ┌───────────────┐ │ │ │ Trip ├─────────────▶│ PaymentMethod │ │ │ └──────┘ └───────────────┘ │ │ │ └─────────────────────────────────────────────┘
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
Carexists first and is passed in. If the driver object goes away, the car object is still there. - Composition:
Tripcreates its own stops in the constructor. Nothing outside holds them, so they go away with the trip. - Association:
Tripkeeps a link to aRiderthat lives on its own. - Dependency:
PaymentMethodis only a parameter.Triphas no field for it.
Multiplicity: how many
A number at each end of a line says how many objects take part.
┌────────────────────────────────────────────┐ │ │ │ ┌───────┐ 1 0..* ┌──────┐ │ │ │ Rider ├────────────────┤ Trip │ │ │ └───────┘ └──────┘ │ │ ┌────────┐ 1 1..* ┌───────┐ │ │ │ Driver │○──────────────┤ Car │ │ │ └────────┘ └───────┘ │ │ │ └────────────────────────────────────────────┘
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.
┌────────────────────────────────────────────────────────┐ │ │ │ ┌──────┐ │ │ │ User │ │ │ └──────┘ │ │ △ │ │ ┌─────────┴─────────┐ │ │ ┌────┴────┐ ┌───┴────┐ 1 1..* ┌─────┐ │ │ │ Rider │ │ Driver │○──────────┤ Car │ │ │ └────┬────┘ └───┬────┘ └─────┘ │ │ │ 1 │ 1 │ │ │ │ │ │ │ 0..* │ 0..* │ │ ┌─┴───────────────────┴──┐ │ │ │ Trip │ ┌───────────────┐ │ │ ├────────────────────────┤ │ <<interface>> │ │ │ │ + end(method) ├──▶│ PaymentMethod │ │ │ └────────────────────────┘ └───────────────┘ │ │ ● △ │ │ │ 2..* ╎ │ │ ┌───┴──┐ ┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ │ │ Stop │ ┌──────┴─────┐ ┌──────┴──────┐ │ │ └──────┘ │ UpiPayment │ │ CardPayment │ │ │ └────────────┘ └─────────────┘ │ │ │ │ ── knows ○── has ●── owns ──▶ uses │ │ ──▷ extends ╌╌▷ implements │ │ │ └────────────────────────────────────────────────────────┘
RiderandDriverextendUser.- 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
PaymentMethodwhen it ends. CardPaymentandUpiPaymentimplementPaymentMethod. 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.
| Relationship | Symbol | Test question | Ride 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.
- Nouns become boxes. Rider, trip, pickup and drop (one
Stopclass), driver, car, card and UPI. - Verbs become methods. "books" is
Rider.book(), "accepts" isDriver.accept(trip), "pays" isPaymentMethod.pay(amount). - Add only the key fields. The fare and status of a trip, the plate of a car.
- Draw the relationships last. For each pair, ask the test questions from the table.
- Add multiplicity where the number changes the design, such as one car or many.
- 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.
- 01
A class box has three parts: the name, the fields and the methods.
-is private,+is public. - 02
Aggregation (○──) receives a part that lives on its own. Composition (●──) creates the part, and the part dies with the whole.
- 03
Inheritance (──▷) means "is a". Implementation (╌╌▷) keeps an interface's promise. Dependency (──▶) uses a class inside one call.
- 04
In an interview: nouns to boxes, verbs to methods, relationships last, and multiplicity where it matters.
Read more
- Class diagrams in the Mermaid docs, every relationship and multiplicity in text form
- Creating diagrams in the GitHub docs, to draw them in a README
- UML as sketch by Martin Fowler, on drawing only what helps the talk
- The UML specification from the Object Management Group, the full standard
Next: the single responsibility principle, the first of the five SOLID rules.