Master Practical Java Architecture & Design Patterns!
Subscribe to Ram N Java for clean coding tutorials, GoF design patterns, and real-world system implementations!
🔔 Subscribe to Ram N JavaThe Vending Machine Challenge
A classic interview and system design challenge is the Vending Machine. A vending machine moves through multiple states:
- No Coin Inserted: Waiting for payment before accepting a product selection.
- Coin Inserted: Validates credit, allows selection, or refunds the coin upon cancellation.
- Dispensing Item: Releases the chosen product and checks if stock remains.
- Sold Out: Disables purchases when the inventory is empty.
Writing this with nested if-else or switch-case statements turns every user action (insert coin, select item, press cancel) into complex branching logic where state transitions are brittle and error-prone.
Architecture of the State Pattern Solution
The State Design Pattern encapsulates state-specific behaviors into individual classes, letting the vending machine change its behavior dynamically:
-
State Interface: Outlines the key vending actions:
insertCoin(),pressButton(), anddispense(). -
Concrete State Classes: Classes such as
NoCoinState,HasCoinState,DispensingState, andSoldOutStateimplement the actions valid for that state and transition the machine context to the next state. - Context Class (VendingMachine): Holds the inventory, maintains a reference to the active state, and simply delegates incoming user requests to the current state object.
Key Architectural Benefits
Implementing the vending machine with the State pattern delivers immediate engineering advantages:
- Elimination of Massive Conditionals: Replaces deep conditional trees with clean, polymorphic method calls.
- Single Responsibility Principle: Each state’s transition rules and actions are isolated in their own class.
- Open/Closed Principle: Adding new states (e.g., maintenance mode, contactless pay) does not alter existing operational state classes.
Related Tutorials from Ram N Java
Click here to watch in Youtube : https://www.youtube.com/watch?v=VIzWKPRG9lo
Click the below Image to Enlarge
| State Design pattern - Implementation [Vending Machine] |
| State Design pattern - Implementation [Vending Machine] - Class Diagram |
VendingMachineState.java
{
@Override
public void selectProductAndInsertMoney( int amount, String productName )
{
System.out.println("Already Vending machine has money and product selected,So wait till it finish the current dispensing process ... ");
}
@Override
public void dispenseProduct()
{
System.out.println("Vending Machine dispensed the product ...");
}
}
NoMoneyState.java
public class NoMoneyState implements VendingMachineState
{
@Override
public void selectProductAndInsertMoney( int amount, String productName )
{
System.out.println(amount + "Rs has been inserted and " + productName + " has been selected...");
}
@Override
public void dispenseProduct()
{
System.out.println("Vending Machine cannot dispense product because money is not inserted and product is not selected...");
}
}
VendingMachine.java
// This is like Context
public class VendingMachine implements VendingMachineState
{
private VendingMachineState vendingMachineState;
public VendingMachine()
{
vendingMachineState = new NoMoneyState();
}
public VendingMachineState getVendingMachineState()
{
return vendingMachineState;
}
public void setVendingMachineState( VendingMachineState vendingMachineState )
{
this.vendingMachineState = vendingMachineState;
}
@Override
public void selectProductAndInsertMoney( int amount, String productName )
{
vendingMachineState.selectProductAndInsertMoney(amount, productName);
VendingMachineState hasMoneyState = new HasMoneyState();
/*
* Money has been inserted so vending Machine changed the
* internal state to 'hasMoneyState'
*/
if( vendingMachineState instanceof NoMoneyState )
{
setVendingMachineState(hasMoneyState);
System.out.println("VendingMachine internal state has been moved to : "
+ vendingMachineState.getClass().getName());
}
}
@Override
public void dispenseProduct()
{
VendingMachineState noMoenyState = new NoMoneyState();
vendingMachineState.dispenseProduct();
/*
* Product has been dispensed so vending Machine changed the
* internal state to 'NoMoneyState'
*/
if( vendingMachineState instanceof HasMoneyState )
{
setVendingMachineState(noMoenyState);
System.out.println("VendingMachine internal state has been moved to : "
+ vendingMachineState.getClass().getName());
}
}
}
Client.java
public class Client
{
public static void main( String[] args )
{
/*
* Initially Vending Machine will be 'noMoneyState'
*/
VendingMachine vendingMachine = new VendingMachine();
System.out.println("Current VendingMachine State : "
+ vendingMachine.getVendingMachineState().getClass().getName()+"\n");
vendingMachine.dispenseProduct();
vendingMachine.selectProductAndInsertMoney(100, "Pepsi");
/*
* Money has been inserted so vending Machine changed the
* internal state to 'hasMoneyState'
*/
System.out.println("\nCurrent VendingMachine State : "
+ vendingMachine.getVendingMachineState().getClass().getName()+"\n");
vendingMachine.selectProductAndInsertMoney(100, "Fanta");
vendingMachine.dispenseProduct();
/*
* Product has been dispensed so vending Machine changed the
* internal state to 'NoMoneyState'
*/
System.out.println("\nCurrent VendingMachine State : "
+ vendingMachine.getVendingMachineState().getClass().getName());
}
}
Output
Current VendingMachine State : NoMoneyState
Vending Machine cannot dispense product because money is not inserted and product is not selected...
100Rs has been inserted and Pepsi has been selected...
VendingMachine internal state has been moved to : HasMoneyState
Current VendingMachine State : HasMoneyState
Already Vending machine has money and product selected,So wait till it finish the current dispensing process ...
Vending Machine dispensed the product ...
VendingMachine internal state has been moved to : NoMoneyState
Current VendingMachine State : NoMoneyState