Design a Vending Machine (Low-Level Design)
Design a vending machine in an LLD interview — the textbook State pattern problem. States (Idle, HasMoney, Dispensing, OutOfStock), inventory, the change algorithm, classes, UML, and key decisions.
🟠Low-Level Design · Case Study · Senior
"Design a vending machine" is the textbook problem for the State pattern — because a vending machine is a state machine. It behaves completely differently depending on whether it's idle, holding your money, or dispensing. Model that cleanly and the whole design falls into place. Let's build it.
Step 1 — Requirements
- Functional: user selects a product, inserts money (coins/notes), machine dispenses if enough money and in stock, returns change; refund if cancelled; admin can restock.
- Non-functional: never dispense without payment, never give wrong change, handle out-of-stock and insufficient funds gracefully.
Step 2 — The classes
- VendingMachine — the context; holds the current state, inventory, and inserted balance.
- State (interface) with concrete states: IdleState, HasMoneyState, DispensingState, OutOfStockState — each implements the machine's actions.
- Product and Inventory (product → count, price).
- Coin / Note enums for denominations; a change calculator.
Step 3 — The state machine (the heart)
interface State { void insertMoney(int amt); void selectProduct(String id); void dispense(); }
class IdleState implements State {
private final VendingMachine m;
public void insertMoney(int amt){ m.addBalance(amt); m.setState(m.hasMoney); }
public void selectProduct(String id){ System.out.println("Insert money first"); }
public void dispense(){ /* nothing */ }
}
class HasMoneyState implements State {
public void selectProduct(String id){
if (m.inStock(id) && m.balance() >= m.price(id)) m.setState(m.dispensing);
else System.out.println("Out of stock or insufficient funds");
}
// …
}
class VendingMachine { // the context
State idle, hasMoney, dispensing; // one instance each
private State state = idle;
void setState(State s){ this.state = s; }
void insertMoney(int a){ state.insertMoney(a); } // delegate to state
void selectProduct(String id){ state.selectProduct(id); }
}
Each state enforces its own rules — you can't select a product in Idle, can't insert money mid-dispense. The invalid transitions simply don't exist, which is exactly what makes the State pattern shine here.
Step 4 — The tricky bit: change
🔴 Returning change is a mini algorithm: given an amount, dispense the fewest coins/notes from available denominations (a greedy approach for standard denominations). Edge case interviewers love: what if you can't make exact change? — refuse the sale and refund, or warn "exact change only." Calling this out shows thoroughness.
Step 5 — Other decisions
🟠Keep inventory as a map of product → (price, count); decrement on dispense and flip to OutOfStock when a slot empties. Handle cancel (refund the balance and return to Idle). If it's multi-threaded (real machines aren't, but interviewers ask), guard the balance/inventory. Enums for denominations avoid magic numbers.
The interview-ready walkthrough
"A vending machine is a State machine, so I'd model states — Idle, HasMoney, Dispensing, OutOfStock — each a class implementing the actions (insert money, select, dispense), with the VendingMachine context delegating to the current state and holding inventory + balance. Invalid transitions (selecting before paying) are impossible by design. I'd keep inventory as product→(price,count), compute change greedily and handle the can't-make-exact-change case, and support cancel/refund. Enums for denominations." That's the State pattern applied crisply — the whole point of the question."
What to read next
- State Pattern — the engine of this design
- Design an ATM — a close cousin (states + cash dispensing)
- Design Tic-Tac-Toe — another compact LLD
- ← The complete System Design guide (hub)