Vending Machine LLD2026-05-14
Deep Dive: Vending Machine Problem (State Pattern)
This low-level design models a vending machine with clear state transitions:
NoMoneyState: Waiting for user input and money insertion.HasMoneyState: User can insert more money, eject money, or select a product.DispenseState: Machine dispenses a product and updates balance and stock.SoldOutState: Machine blocks operations when all products are out of stock.
The machine delegates behavior to the active state object, keeping each state focused and avoiding large conditional blocks.
Full Implementation
from abc import ABC, abstractmethod
class State(ABC):
@abstractmethod
def insert_money(self, amount):
pass
@abstractmethod
def eject_money(self):
pass
@abstractmethod
def select_product(self, name):
pass
@abstractmethod
def dispense_product(self, name):
pass
class SoldOutState(State):
def __init__(self, machine):
self.machine = machine
def insert_money(self, amount):
print("Machine is sold out.")
def eject_money(self):
print("Machine is sold out.")
def select_product(self, name):
print("Machine is sold out.")
def dispense_product(self, name):
print("Machine is sold out.")
class NoMoneyState(State):
def __init__(self, machine):
self.machine = machine
def insert_money(self, amount):
self.machine.balance += amount
print(f"Inserted: {amount}. Current balance: {self.machine.balance}")
self.machine.set_state(self.machine.has_money_state)
def eject_money(self):
print("No money to return.")
def select_product(self, name):
print("Insert money first.")
def dispense_product(self, name):
print("Select a product first.")
class HasMoneyState(State):
def __init__(self, machine):
self.machine = machine
def insert_money(self, amount):
self.machine.balance += amount
print(f"Balance updated: {self.machine.balance}")
def eject_money(self):
print(f"Returned: {self.machine.balance}")
self.machine.balance = 0
if self.machine.is_out_of_stock():
self.machine.set_state(self.machine.sold_out_state)
return
self.machine.set_state(self.machine.no_money_state)
def select_product(self, name):
if name not in self.machine.items:
print("Invalid product.")
return
price, quantity = self.machine.items[name]
if quantity <= 0:
print("Product out of stock.")
return
if self.machine.balance < price:
print(f"Insufficient balance. Add {price - self.machine.balance} more.")
return
self.machine.set_state(self.machine.dispense_state)
self.machine.dispense_product(name)
def dispense_product(self, name):
print("Select a product before dispensing.")
class DispenseState(State):
def __init__(self, machine):
self.machine = machine
def insert_money(self, amount):
print("Dispensing in progress. Please wait.")
def eject_money(self):
print("Cannot eject money while dispensing.")
def select_product(self, name):
print("Already dispensing a product.")
def dispense_product(self, name):
price, quantity = self.machine.items[name]
self.machine.items[name][1] = quantity - 1
self.machine.balance -= price
print(f"Dispensed: {name}")
print(f"Remaining balance: {self.machine.balance}")
if self.machine.is_out_of_stock():
self.machine.set_state(self.machine.sold_out_state)
return
if self.machine.balance > 0:
self.machine.set_state(self.machine.has_money_state)
else:
self.machine.set_state(self.machine.no_money_state)
class VendingMachine:
def __init__(self, items):
self.no_money_state = NoMoneyState(self)
self.has_money_state = HasMoneyState(self)
self.dispense_state = DispenseState(self)
self.sold_out_state = SoldOutState(self)
self.items = items
self.balance = 0
if self.is_out_of_stock():
self.current_state = self.sold_out_state
else:
self.current_state = self.no_money_state
def set_state(self, state):
self.current_state = state
def insert_money(self, amount):
self.current_state.insert_money(amount)
def eject_money(self):
self.current_state.eject_money()
def select_product(self, name):
self.current_state.select_product(name)
def dispense_product(self, name):
self.current_state.dispense_product(name)
def is_out_of_stock(self):
return all(quantity == 0 for _, quantity in self.items.values())
if __name__ == '__main__':
inventory = {
'coke': [5, 2],
'chips': [3, 1],
'water': [2, 3],
}
machine = VendingMachine(inventory)
machine.insert_money(5)
machine.select_product('chips')