Behavioral Patterns • State PatternMedium⏱️ ~3 min
Applying State Pattern to a Vending Machine
Domain: Vending Machine
A vending machine has four states: Idle (waiting for money), HasMoney (money inserted), Dispensing (delivering product), and OutOfStock. Each state handles user actions differently.
Class Design
«interface»
VendingMachineState
+ insertMoney(amount): void
+ selectProduct(id): void
+ dispense(): void
+ cancel(): void
+ selectProduct(id): void
+ dispense(): void
+ cancel(): void
▲
IdleState
+ insertMoney()
+ selectProduct()
+ dispense()
+ cancel()
+ selectProduct()
+ dispense()
+ cancel()
HasMoneyState
+ insertMoney()
+ selectProduct()
+ dispense()
+ cancel()
+ selectProduct()
+ dispense()
+ cancel()
DispensingState
+ insertMoney()
+ selectProduct()
+ dispense()
+ cancel()
+ selectProduct()
+ dispense()
+ cancel()
OutOfStockState
+ insertMoney()
+ selectProduct()
+ dispense()
+ cancel()
+ selectProduct()
+ dispense()
+ cancel()
◆
VendingMachine
- currentState: State
- inventory: Map
- balance: Money
- inventory: Map
- balance: Money
+ setState(state): void
+ insertMoney(amt): void
+ selectProduct(id): void
+ dispense(): void
+ cancel(): void
+ insertMoney(amt): void
+ selectProduct(id): void
+ dispense(): void
+ cancel(): void
State-Specific Behavior
IdleState:
insertMoney(): Accept money, transition to HasMoneyStateselectProduct(): Reject (no money inserted yet)dispense(): Reject (invalid operation)cancel(): Do nothing (no transaction active)
HasMoneyState:
insertMoney(): Add to balance, stay in HasMoneyStateselectProduct(): Validate product and price, transition to DispensingState if valid, but if insufficient funds or out of stock, display error and stay in HasMoneyStatedispense(): Reject (product not selected yet)cancel(): Refund money, transition to IdleState
DispensingState:
insertMoney(): Reject (dispensing in progress)selectProduct(): Reject (already dispensing)dispense(): Deliver product, update inventory, check if any stock remains (if yes, transition to IdleState, but if no stock, transition to OutOfStockState)cancel(): Reject (too late to cancel)
OutOfStockState:
- All operations: Reject with "Out of Stock" message
- Special:
restock()method transitions back to IdleState
State Transition Example
// In HasMoneyState.selectProduct()
if (product exists AND balance >= price AND inStock) {
context.setState(new DispensingState())
} else if (NOT inStock) {
context.refund()
context.setState(new OutOfStockState())
} else {
display "Insufficient funds"
// Stay in HasMoneyState
}
if (product exists AND balance >= price AND inStock) {
context.setState(new DispensingState())
} else if (NOT inStock) {
context.refund()
context.setState(new OutOfStockState())
} else {
display "Insufficient funds"
// Stay in HasMoneyState
}
Interview Tip: In machine coding rounds, interviewers look for how you handle invalid state transitions (like canceling during dispensing). Each state should explicitly handle or reject every possible operation.
💡 Key Takeaways
✓Each state implements all operations but behaves differently
✓States trigger transitions based on business logic (inventory, balance)
✓Invalid operations in a state are explicitly rejected, not ignored
✓Vending machine context holds inventory and balance data
✓State transitions handle edge cases like out of stock during purchase
📌 Examples
1Idle to HasMoney when money inserted
2HasMoney to Dispensing when valid product selected
3Dispensing to OutOfStock if last item sold
4HasMoney to Idle when cancel requested