Course Content
Object-Oriented Design Interview
14 sections · 29 lessons
The five-phase script: finding classes, assigning responsibilities and coding the core
By minute ten you have a short problem statement and three written lists. The next thirty minutes turn them into a model and code: Phase 3 names the classes, Phase 4 decides what each one is responsible for, and Phase 5 writes the few methods where the design shows. This lesson keeps the car rental example from the previous one running through all three.
Phase 3: finding classes
Fifteen minutes, and the first ten of them have no drawing in them. You name objects in words first.
Start with noun extraction
Underline every noun in your requirements. For the car rental list:
search available cars by class and date range · reserve a car · convert reservation to an active rental · compute charge · price by class, duration, discount · cancel a reservation
Candidate nouns: Car, CarClass, DateRange, Reservation, Rental, Charge, Price, Discount, Customer, Branch.
Noun extraction is a starting heuristic and nothing more. It over-produces, badly. Left unfiltered you get a class named Charge and a class named Price that both hold one number, and no one can say which is which.
The three filters
Run every candidate noun through three questions. It survives only if it passes at least two.
1. Does it have state that outlives a single method call? Car has a registration number, a class, and a status. Survives. Charge is a number computed at return time. Fails — it is a return value, not a class.
2. Does it have behaviour of its own? Reservation can be cancelled, can expire, can convert to a rental. Survives. CarClass (economy, SUV, luxury) has no behaviour and a fixed set of values. Fails as a class — becomes an enum.
3. Does it change independently of everything else? Pricing rules change on a different schedule from the car fleet, so pricing wants to be its own thing even though "price" is a number. Survives, as a strategy. DateRange is two dates that always travel together and are validated together — a small value class, worth keeping precisely because it stops you passing two loose dates everywhere.
Applying the filters
| Candidate | State? | Behaviour? | Changes alone? | Verdict |
|---|---|---|---|---|
| Car | yes | some | no | Class |
| CarClass | no | no | no | Enum |
| Reservation | yes | yes | yes | Class |
| Rental | yes | yes | yes | Class |
| Charge | no | no | no | Return value — a Money field on Rental |
| Price / Discount | no | yes | yes | Strategy interface — PricingPolicy |
| DateRange | yes | yes | no | Value class |
| Customer | yes | little | yes | Class |
| Branch | yes | yes | — | Cut — single branch, per Phase 1 |
Nine candidate nouns became five classes, one enum, one value class, and one interface. That filtering step is the visible work of Phase 3, and doing it out loud is most of the score: "Charge I'm not making a class — it's a computed value with no state of its own. Price I am, because pricing rules are the thing most likely to change."
The two mistakes at this step
Too few classes. Everything becomes a field on the biggest noun, and you have the god class from The failure modes.
Too many classes. Every noun survives, including Charge and Price and ReservationStatus as a class rather than an enum, and now there are eighteen boxes and no time to draw the lines. Eighteen boxes with unclear relationships reads as worse than eight with clear ones.
Six to nine classes is the right size for a 45-minute problem. If you are past twelve, you have kept something that should have been an enum or a field.
Phase 4: assigning responsibilities
This is the most heavily scored skill in the round. Two candidates can produce identical boxes and get completely different scores based on what lives inside them.
The one-sentence test
For each class, state its job in one sentence. If the sentence needs an "and", the class is wrong.
Car — holds the identity and current status of one vehicle.Reservation — holds one customer's claim on a car for a date range, and can be cancelled.Rental — tracks an in-progress rental from pickup to return, and computes its charge.Fleet — answers "which cars of this class are free between these dates?"PricingPolicy— turns a car class and a duration into an amount.Two of those have an "and" in them, so look harder.
Reservation: "holds a claim, and can be cancelled". Cancellation is a change to the claim's own state, so that is one job stated in two clauses. Keep it.
Rental: "tracks a rental and computes its charge". That "and" is real. Computing a charge needs pricing rules, discount rules, and possibly tax rules — a whole area of change that has nothing to do with tracking an odometer reading. Move it out. Rental asks PricingPolicy for the amount; it does not know the rules.
That is the entire test, and it caught a genuine design error in five seconds.
CRC cards on a whiteboard
CRC stands for Class, Responsibilities, Collaborators. It is a 1980s technique that survives because it takes ninety seconds and fits on a sticky note.
Draw a box divided into three:
┌──────────────────────────────────────┐│ Rental │ <- class name├──────────────────┬───────────────────┤│ track pickup / │ Car │ <- collaborators, right│ return times │ Customer ││ close the rental │ PricingPolicy ││ ask for a charge │ │└──────────────────┴───────────────────┘ ^ responsibilities, leftThree rules make CRC cards useful rather than busywork:
- Responsibilities are verbs, three to five per card. More than five means split.
- Collaborators are the classes this one talks to. If a card lists six collaborators, it is a coordinator that probably knows too much.
- Write them before the diagram. Cards are cheap to tear up; a diagram with fifteen lines is not.
Where responsibilities belong: the two rules
Put behaviour with the data it needs. If a method reads three fields of Board and none of its own, it belongs on Board. This single rule fixes most misplacements. It is why win detection belongs on the board rather than the game (the tic tac toe objects), and why scoring belongs on the hand rather than the player (the blackjack objects).
Put policy behind an interface. Anything that is a rule someone might change — pricing, scheduling, allocation, discounting — goes behind an interface so the change is a new class rather than an edit. Anything that is a fact about the world — a car has a registration number — is a field.
Phase 5: coding the core, and what to skip
Fifteen minutes buys you two or three methods. Choosing which two is a design decision, and interviewers score it.
Which methods to write
Write the methods where the design shows — where a bad model would produce ugly code and a good one produces short code.
| Problem | The method worth writing | Why |
|---|---|---|
| Parking lot | assignSpot(vehicle) | Shows the availability structure and the size matching |
| Movie booking | hold(showSeats) | Shows how double-booking is prevented |
| Vending machine | State.insertCoin() on two states | Shows the state pattern doing its job |
| Elevator | dispatch(hallRequest) | Shows the scheduling strategy |
| Tic tac toe | Board.recordMove() | Shows the O(1) win check |
The pattern: pick the method that touches the constraint from your non-functional requirements. That is the one where the design has to actually work.
Say this choice out loud: "I'll write spot assignment, because that's where the size matching and the concurrency question both land. Ticket printing I'll skip — it's a formatting method and it won't tell you anything."
What to skip, and how to say so
Skip these, every time, out loud rather than silently:
- Getters and setters. Write
// getters omittedin the class body and move on. Nobody has ever scored a point for a getter. - Constructors, unless a constructor enforces an invariant worth showing.
- Persistence. "I'll assume a
SpotRepositoryinterface withfindAvailableandsave. Implementation is a database or a map, doesn't change the design." - Input/output. No console reading, no formatting, no user interface.
equalsandhashCode, unless objects go into aHashSetor a map key, in which case say "these need equals and hashCode on the registration number" and move on.- Exception classes. Declare
throws SpotUnavailableExceptionand do not define it.
The phrase to memorise is "I'll assume an interface here". It converts an unwritten component into a stated boundary, which is what an interviewer wants anyway.
What "complete" means for the code you do write
The two or three methods you write should be real. Complete signature, real field accesses, real branches, correct null and error handling on the paths that matter. Pseudocode in the methods that carry the design reads as not knowing how to write it.
1// Real enough. This is the level of completeness expected.2public Ticket park(Vehicle vehicle) {3 ParkingSpot spot = spotAllocator.allocate(vehicle.getSize())4 .orElseThrow(() -> new NoSpotAvailableException(vehicle.getSize()));5 Ticket ticket = new Ticket(nextTicketId(), vehicle, spot, clock.now());6 activeTickets.put(ticket.getId(), ticket);7 return ticket;8}Four lines, and they show the allocator boundary, the failure path, the clock being injected rather than called statically, and where tickets live.