Three OOD Classics, Worked
The method from the OOD interview page, run on the three prompts that show up most. The parking lot end to end — requirements, the nouns pass, the class model, and the three decisions that actually get graded: spot assignment as a strategy, the ticket as the identity object, fee calculation as a swappable strategy, with the real compact code. Then the reveal that collapses half the catalog: the vending machine and the elevator are the same problem — state machines with states, events, guards, and actions. Drive one live: insert coins, select, dispense; request floors up and down; watch the legal transitions fire and the illegal ones bounce with the guard named. Finish with the missing-guard bugs interviewers probe, and the move that spots a state machine in any prompt.
Concept · Interview Craft. The source ↗
A free, interactive, animated visual explainer of Three OOD Classics, Worked — built to be understood, not skimmed.
Questions
- How do you design a parking lot in an OOD interview?
- Start by clarifying scope, then run the nouns/verbs pass on the requirements. The concrete nouns become your core classes — ParkingLot, Level, ParkingSpot, Vehicle (with Car/Motorbike/Truck subtypes), and Ticket — while a spot's size is a small enum, not a class. Then wire the relationships with composition: a lot has levels, a level has spots, a ticket refers to a spot and a vehicle. The part that actually gets graded is three decisions. First, how a spot gets assigned: put that behind a strategy so "nearest first" can be swapped for "fill by level" without touching the parking code. Second, the ticket is the identity object — it is what the exit gate looks up, so it carries the entry time and the spot, and it, not a license-plate string, is the thing that proves a car is owed a charge. Third, fee calculation is another strategy, so flat-rate, hourly, and surge pricing are interchangeable. State the invariants out loud as you go — a spot holds at most one vehicle, a ticket is paid before exit — and place a guard for each. That combination of a clean model plus swappable strategies plus stated invariants is a strong parking-lot answer.
- Why model a vending machine as a state machine instead of boolean flags?
- Because a vending machine's legal actions depend entirely on what mode it is in, and boolean flags can't enforce that. The flag approach — has_coins, item_selected, is_dispensing — quickly drifts out of sync: nothing stops the code from reaching the dispense branch while has_coins is false, so an illegal "dispense without payment" slips through a gap between the if-checks. Modeling it as an explicit state machine makes that impossible by construction. You name the states (idle, paid, item-selected, vending), the events (insert coin, select, dispense, refund), and put a guard on each transition — a condition that must hold for the move to be allowed. "Dispense" is only defined as a transition out of the item-selected state, so in the idle state the event simply has nowhere to go and is refused, with the guard naming why ("pay first — need 75¢, have 25¢"). The states also give every action a clear home, so change-making and refunds live where they belong instead of scattered across flag checks.
- What are the states and guards of an elevator in a low-level design?
- The core states are idle, moving-up, moving-down, and doors-open, with the current floor and the set of pending requests as context. The events are a floor request, a step (advancing one floor), and opening or closing the doors. The guards are where the design earns its grade, and the sharpest one is "do not reverse direction mid-travel": if the elevator is moving up with requests still above it, a new request for a floor below does not flip the direction — it is queued and served on the way back down. A second critical guard is "the doors never open while moving between floors" — the open-doors event is only legal once the car has arrived at a floor and stopped. Modeled as a state machine, a request that would reverse direction mid-travel is rejected or deferred rather than silently obeyed, and trying to open the doors in a moving state is refused outright. The scheduling policy itself — nearest-request-first versus strict up-then-down (the elevator, or SCAN, algorithm) — is best held as a swappable strategy, because interviewers love to ask you to change it.
- How do you spot that an interview prompt is really a state machine?
- Listen for the phrase "in this situation you can do X but not Y." The moment an object's allowed actions depend on its current condition — not just its data, but a mode it is in — you are looking at a state machine, and you should say so immediately, because naming it hands you the whole skeleton of the answer. The tells are everywhere in the classic prompts: an order moves cart → placed → paid → shipped → delivered and can never skip or go backward; an ATM insists on card-inserted → authenticated → transacting in that order; a document goes draft → in-review → published; a turn-based game only lets a player move on their own turn; a traffic light cycles green → yellow → red. Each is defined by states, the events that move between them, and guards that forbid the illegal moves. Once you spot it, the design writes itself: enumerate the states, list the legal transitions, and put a guard on each — and the tangle of boolean flags that sinks so many of these answers never appears.
- Should spot assignment and fee calculation be separate classes in a parking lot?
- Yes — both are textbook uses of the Strategy pattern, and pulling them out is exactly the kind of clean seam an interviewer is looking for. Spot assignment is a policy that can vary: nearest-to-entrance, fill-lowest-level-first, or reserve-large-spots-for-trucks are all reasonable, and an interviewer will often ask you to change it. If that logic lives inside ParkingLot.park(), changing it means editing the parking code and risking the rest; if it lives behind a SpotAssigner interface the lot holds and delegates to, you swap one object and nothing else moves. Fee calculation is the same shape — flat rate, hourly, daily maximum, surge pricing — so a FeeStrategy interface lets the pricing scheme change without touching the code that charges the customer. This is the concrete payoff of "favor composition over inheritance": rather than an HourlyParkingLot subclass, the lot is composed of an assigner and a pricing strategy it can swap at runtime. Keeping them separate also honors the single-responsibility principle — the lot manages spots and tickets, and the decisions about how to assign and how to charge live in their own small, testable classes.