Course Content
Object-Oriented Design Interview
14 sections · 29 lessons
Vending Machine: requirements and why State beats a flag
The vending machine is the textbook State pattern problem, and it is kept small on purpose so that one skill is tested without noise. This lesson covers the prompt, the requirements and invariants, and the argument for the State pattern — made the honest way, by writing the flag-and-if-else version first and watching it collapse.
Why this problem is kept small
A vending machine has four or five classes and no domain subtlety. That is deliberate: it is the textbook State pattern problem, and interviewers use it when they want to test one thing without noise. If your design handles the states cleanly, you pass; if it has a status field and a growing pile of if statements, you do not.
Because the domain is so small, the whole round becomes about lifecycle. Expect the interviewer to spend most of the time on transitions, and to attack with events out of order: "What happens if the user presses dispense before inserting money? What if they insert money with nothing selected? What if they cancel after paying?"
The four questions that change the model
1. "Which payment methods — coins only, notes, cards?" Coins and notes are synchronous: the money is in the machine the instant it is inserted. A card payment is asynchronous — you request authorisation and wait, and the machine needs a "waiting for authorisation" state and a timeout. Assume cash only for the core design and raise cards in the extensions; that is what the interviewer expects.
2. "Does it give change, and what if it cannot?" This is the question with real substance. If the machine cannot make exact change it must either refuse the sale before taking the money or refuse to complete it and refund. Assume yes, it gives change, and that the coin problem in the section's extensions is in scope.
3. "What happens if the user cancels mid-transaction?" Assume a full refund of whatever has been inserted. This creates the transition that makes the if-else design collapse, so ask it early.
4. "Is restocking and a maintenance mode in scope?" Usually out of scope for the core, but worth naming, because "the machine is being restocked" is another state and adding it to a finished design is a good extension question.
Assumptions this lesson makes
- Cash only: a fixed set of coin denominations plus small notes.
- The machine holds several item slots, each with a code, a price, and a quantity.
- One item per transaction.
- Change is given when possible; if it is not possible, the transaction is refused and the money returned.
- One user at a time — a physical machine has one slot and one set of buttons. This makes the concurrency question different from the parking lot's, and the section's extensions address it.
Requirements
Functional requirements
1. Select an item by its slot code2. Insert money, one coin or note at a time, accumulating a balance3. Dispense the item when the balance covers the price4. Return change5. Cancel at any point and refund everything inserted6. Refuse invalid actions clearly (sold out, insufficient funds, wrong order of operations)Requirement 6 is unusual and it is worth writing down. In most designs, invalid input is an afterthought; here, the rejections are half the specification. "Dispense pressed with no item selected" is not an error condition to be handled somewhere generic — it is a defined behaviour of a specific state, and the interviewer will ask about it.
Non-functional requirements
N1. The machine must never dispense without full payment. A one-way correctness rule.
N2. The machine must never keep money without dispensing or refunding. The other direction, and the one people forget. Every path through the machine ends with either goods plus change, or a full refund. There is no third ending.
N3. Adding a state must not require editing every method. This is what forces the State pattern, below.
What is cut
CUT (named, not forgotten)- Card and mobile payments (extensions)- Remote telemetry, sales reporting, and stock alerts to a back office- Multi-item purchases in one transaction- The physical hardware protocol — motors, sensors, coin validatorsTreat hardware as an interface: Dispenser.release(slotCode) and CoinHopper.payOut(coins). Say so: "I'll model the hardware as two interfaces so the machine logic is testable without a machine." That is the same move Section 13 makes for the ATM, and it is worth a point in both.
The invariant to state out loud
There is one sentence that summarises N1 and N2 together, and stating it early sets up everything that follows:
"The invariant is that money and goods move together. At every moment, the machine's obligation to the user is either 'I owe you an item and change' or 'I owe you your money back' — and every transition has to preserve that."
An interviewer hearing that sentence in minute eight knows how the rest of the round will go.
The states, and why State beats a flag
Write the bad version first. It is the fastest way to understand what the pattern is for, and it is what your own first attempt probably looked like.
Attempt one: a status field and if-else
1public class VendingMachine {2 private Status status = IDLE; // IDLE, ITEM_SELECTED, PAID3 private Item selected;4 private Money balance = Money.ZERO;56 public void selectItem(String code) {7 if (status == IDLE) {8 selected = inventory.get(code);9 status = ITEM_SELECTED;10 } else {11 throw new IllegalStateException("item already selected");12 }13 }1415 public void insertMoney(Money amount) {16 if (status == ITEM_SELECTED) {17 balance = balance.plus(amount);18 if (balance.isAtLeast(selected.getPrice())) status = PAID;19 } else {20 throw new IllegalStateException("select an item first");21 }22 }2324 public Item dispense() {25 if (status == PAID) { … } else { throw new IllegalStateException("not paid"); }26 }27}Three states, three methods, and it is readable. So far there is no problem.
Then the requirements arrive
Add cancel. Every method now needs to consider it, and cancel itself needs three branches — refund nothing from idle, refund nothing from selected, refund the balance from paid.
Add "dispensing". The motor takes two seconds and can jam, so the machine must reject input while it runs. That is a fourth state, and it must be handled in all five methods.
Add "out of service". Now five states.
Add "waiting for card authorisation". Six.
Here is what insertMoney looks like at that point:
1public void insertMoney(Money amount) {2 if (status == OUT_OF_SERVICE) { returnCoin(amount); return; }3 if (status == IDLE) { throw new IllegalStateException("select an item first"); }4 if (status == DISPENSING) { returnCoin(amount); return; }5 if (status == AWAITING_AUTH) { throw new IllegalStateException("card in progress"); }6 if (status == ITEM_SELECTED) {7 balance = balance.plus(amount);8 if (balance.isAtLeast(selected.getPrice())) status = PAID;9 return;10 }11 if (status == PAID) { returnCoin(amount); return; }12}And this same ladder now exists in selectItem, dispense, cancel, refund, and serviceMode. Six states × six methods = thirty-six cases spread across six ladders, with nothing keeping them consistent. Adding a seventh state means finding and editing six methods, and the compiler will not tell you if you miss one. That is the cost, stated concretely — and it is the sentence to say in the interview.
The repair: one class per state
Invert it. Instead of each method knowing every state, each state knows every event.
1public interface State {2 void selectItem(VendingMachine m, String code);3 void insertMoney(VendingMachine m, Money amount);4 void dispense(VendingMachine m);5 void cancel(VendingMachine m);6}One class per state implements all four. IdleState.dispense rejects; PaidState.dispense releases the item. VendingMachine holds a current state and forwards every call to it, and its methods become one line each.
Three concrete gains, worth naming out loud:
- Adding a state adds a file and edits nothing. That is Open/Closed (the SOLID reference).
- The compiler enforces completeness. Implementing
Statemeans answering all four events, so no case can be silently forgotten. - Each state's rules are in one place, so "what does the machine do while dispensing?" is one file, not a search across six.