Bird
Raised Fist0

Design a Vending Machine - States, Product Selection, Payment, Change, Refill

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Vending Machine Design with States, Product Selection, Payment, Change, Refill
40 min ยท medium
medium

Design a vending machine that supports multiple states (Idle, Selection, Payment, Dispensing, OutOfService), allows product selection from a fixed catalog, accepts payment via coins and bills, dispenses change, and supports refilling products and cash. The machine must handle concurrent user interactions and maintenance operations safely.

  • The vending machine has 5 states: Idle, Selection, Payment, Dispensing, OutOfService.
  • Supports product selection from a catalog of products with fixed prices.
  • Accepts coins and bills as payment and dispenses change if overpaid.
  • Supports refilling products and cash by maintenance staff without shutting down.
  • Must handle concurrent user interactions and maintenance operations safely.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Vending Machine States Using Enum
Design an enum to represent the vending machine states and explain why enum is preferred over String constants.
๐Ÿ’ก Hint
Use Java enum to enforce type safety and exhaustive switch-case handling at compile time.
Use Java enum VendingMachineState with constants IDLE, SELECTION, PAYMENT, DISPENSING, OUT_OF_SERVICE - enum provides type safety and prevents runtime typo bugs unlike String constants.
Mid: Names enum usage for states. Senior: Explains how enum enables exhaustive switch-case and prevents invalid state values at compile time.
โš ๏ธCommon Wrong Answer
Using String constants for states leads to runtime bugs due to typos and no compile-time checks.
Step 2
Design Product and Catalog Classes with Immutable Product
Design classes for Product and ProductCatalog. Explain why Product should be immutable and how ProductCatalog manages products.
๐Ÿ’ก Hint
Use final fields in Product to make it immutable; ProductCatalog uses a thread-safe ConcurrentHashMap for product storage.
Product class has final fields (id, name, price) making it immutable to ensure thread safety without synchronization; ProductCatalog uses ConcurrentHashMap<String, Product> for concurrent access and updates.
Mid: Names immutability and ConcurrentHashMap usage. Senior: Explains how immutability avoids synchronization overhead and how ConcurrentHashMap prevents race conditions during concurrent reads/writes.
โš ๏ธCommon Wrong Answer
Mutable Product fields cause inconsistent reads under concurrency; using HashMap leads to race conditions and data corruption.
Step 3
Implement Payment Handling with AtomicInteger and synchronized Methods
Design payment processing to accept coins/bills, track inserted amount, and dispense change. Explain concurrency control for payment state.
๐Ÿ’ก Hint
Use AtomicInteger for insertedAmount to allow atomic increments; synchronize methods that check and update payment completion atomically.
Use AtomicInteger insertedAmount for thread-safe increments; synchronize boolean processPayment(int amount) to atomically check if payment is sufficient and update state to PAYMENT_COMPLETE.
Mid: Names AtomicInteger and synchronized usage. Senior: Traces thread interleaving where without synchronization two threads could both see insufficient payment and accept partial payments leading to inconsistent state.
โš ๏ธCommon Wrong Answer
Using int without synchronization causes lost updates; unsynchronized check-then-act leads to double acceptance or incorrect change calculation.
Step 4
Model Vending Machine State Transitions Using Abstract Class and State Pattern
Design an abstract class VendingMachineStateHandler with concrete subclasses for each state. Explain why abstract class is chosen over interface.
๐Ÿ’ก Hint
Abstract class holds shared instance state and default implementations; interfaces cannot hold instance fields in Java.
abstract class VendingMachineStateHandler holds protected VendingMachine reference and common methods; concrete subclasses override behavior per state. Abstract class chosen because it holds shared instance state and partial behavior, which interfaces cannot hold in Java.
Mid: Names State pattern and abstract class usage. Senior: Explains why interface cannot hold shared state fields and how abstract class enables code reuse and encapsulation of state-specific behavior.
โš ๏ธCommon Wrong Answer
Using interface for state handlers forces duplication of shared state management and breaks encapsulation.
Step 5
Design Refill Mechanism with Composition and Synchronization
Design how maintenance staff can refill products and cash concurrently without disrupting user operations. Explain lifecycle and concurrency control.
๐Ÿ’ก Hint
Use composition: VendingMachine has ProductInventory and CashInventory; synchronize refill methods to prevent race conditions with user operations.
VendingMachine composes ProductInventory and CashInventory (composition: lifecycle bound to VendingMachine). Refill methods are synchronized to atomically update inventory counts preventing inconsistent state during concurrent user purchase.
Mid: Names composition and synchronized refill methods. Senior: Explains thread interleaving where without synchronization, concurrent purchase and refill cause inconsistent inventory leading to overselling or cash mismatch.
โš ๏ธCommon Wrong Answer
Using unsynchronized refill leads to race conditions causing negative inventory or incorrect change dispensing.
๐Ÿ”Œ
API Design Challenge
Design the public APIs for the vending machine system to support product selection, payment processing, dispensing, and refilling while ensuring thread safety and extensibility.
  • Design APIs that clearly separate user operations (selectProduct, insertPayment, dispenseProduct) from maintenance operations (refillProducts, refillCash).
  • Use thread-safe data structures and synchronization to handle concurrent calls to these APIs.
  • Consider using the State pattern to encapsulate state-specific API behavior and transitions.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • Use enum for vending machine states to prevent invalid state values.
  • Immutable Product class with final fields for thread safety.
  • ConcurrentHashMap in ProductCatalog for concurrent access.
  • AtomicInteger and synchronized methods for payment concurrency control.
  • abstract class for state handlers to hold shared instance state.
  • Composition relationship with lifecycle ownership for inventories.
  • synchronized refill methods to prevent race conditions.
  • Volatile state variable for visibility across threads.
  • State pattern for encapsulating state-specific behavior.
  • Explain exact thread interleaving causing race conditions without synchronization.
Good to Have - 30%
  • Use of Optional to handle absent products gracefully.
Bonus - 10%
    โœ…
    Model Answer
    โ–ผ Reveal

    This design uses Java enum for vending machine states to ensure type safety and prevent invalid states. The Product class is immutable with final fields to guarantee thread safety without synchronization overhead. ProductCatalog uses ConcurrentHashMap to allow concurrent reads and updates safely. Payment processing uses AtomicInteger for thread-safe increments and synchronized methods to atomically check and update payment state, preventing race conditions. The State pattern is implemented via an abstract class VendingMachineStateHandler with concrete subclasses for each state, enabling shared state and behavior reuse. The vending machine composes ProductInventory and CashInventory, with synchronized refill methods to prevent race conditions during concurrent maintenance and user operations. The state variable is declared volatile to ensure visibility across threads.

    • Use Java enum for states to enforce compile-time checks and prevent invalid states
    • Make Product immutable with final fields to avoid synchronization overhead
    • Use ConcurrentHashMap in ProductCatalog to allow concurrent safe access and updates
    • Use AtomicInteger for insertedAmount to allow atomic increments from multiple threads
    • Synchronize payment processing methods to atomically check and update payment state
    • Implement State pattern with abstract class to hold shared state and behavior
    • Compose ProductInventory and CashInventory with lifecycle bound to VendingMachine
    • Synchronize refill methods to prevent race conditions between maintenance and user operations
    • Declare state variable volatile to ensure visibility across threads
    • Use enum VendingMachineState for type-safe state representation preventing runtime errors.
    • Immutable Product class with final fields ensures thread safety without synchronization overhead.
    • ProductCatalog uses ConcurrentHashMap for concurrent safe product lookup and updates.
    • AtomicInteger tracks inserted payment amount allowing atomic increments from multiple threads.
    • synchronized methods in payment processing ensure atomic check-and-act preventing race conditions.
    • abstract class VendingMachineStateHandler enables shared state and behavior reuse; interfaces cannot hold instance fields in Java.
    • Composition relationship: VendingMachine owns ProductInventory and CashInventory with lifecycle bound to machine.
    • synchronized refill methods prevent race conditions between maintenance and user operations.
    • State pattern encapsulates state-specific behavior and transitions improving extensibility (Open/Closed principle).
    • Volatile keyword used for state variable in VendingMachine to ensure visibility across threads.

    Practice

    (1/5)
    1. In designing the Player abstraction for the Snake and Ladder game, should Player be an abstract class or an interface, and why?
    easy
    A. Player should be an interface because it only defines behavior without state.
    B. Player should be an abstract class because it holds common state like current position and provides default methods.
    C. Player should be an interface to allow multiple inheritance of behaviors.
    D. Player should be an abstract class to enforce implementation of all methods in subclasses.

    Solution

    1. Step 1: Analyze state and behavior requirements

      The Player has common state such as current position on the board and methods like move().
    2. Step 2: Choose abstraction type

      An abstract class allows sharing state and default method implementations, which interfaces (pre-Java 8) cannot provide.
    3. Final Answer:

      Option B -> Option B
    4. Quick Check:

      Abstract class holds shared state and behavior [check]
    Hint: State + default behavior -> abstract class, not interface [check]
    Common Mistakes:
    2. A class has a private constructor and a clone() method that returns a copy of itself. Which design pattern is applied here and why is this the correct use case?
    easy
    A. Builder pattern, because it separates object construction from representation
    B. Singleton pattern, because it restricts instantiation to one object
    C. Factory pattern, because it creates objects through a factory method
    D. Prototype pattern, because cloning avoids expensive object construction

    Solution

    1. Step 1: Identify the pattern from constructor and clone()

      The private constructor prevents direct instantiation, and the presence of a clone() method indicates object copying rather than new creation.
    2. Step 2: Match pattern intent to scenario

      The Prototype pattern is used to clone objects when construction is expensive, which fits the scenario. Singleton restricts to one instance, Factory creates new instances, and Builder separates construction steps.
    3. Final Answer:

      Option D -> Option D
    4. Quick Check:

      Private constructor + clone() -> Prototype pattern [check]
    Hint: Private constructor + clone() means Prototype pattern [check]
    Common Mistakes:
    3. In the Meeting Scheduler, the RoomBookingManager class has a bookRoom() method that checks if a room is available and then marks it booked. Without synchronizing bookRoom(), which exact sequence of events between Thread 1 and Thread 2 causes a double-booking race condition?
    medium
    A. Thread 1 reads availability as false; Thread 2 reads availability as true; both proceed to book.
    B. Thread 1 and Thread 2 both wait on a lock, causing deadlock and no booking.
    C. Thread 1 books room; Thread 2 reads availability after booking and cancels its booking.
    D. Thread 1 checks isAvailable() -> true, context switch before booking; Thread 2 checks isAvailable() -> true, both book simultaneously.

    Solution

    1. Step 1: Identify check-then-act gap

      Without synchronization, Thread 1 reads availability as true but is preempted before booking; Thread 2 also reads true and proceeds.
    2. Step 2: Both threads book simultaneously causing double-booking

      Because the availability check and booking are not atomic, both threads can book the same room concurrently.
    3. Final Answer:

      Option D -> Option D
    4. Quick Check:

      Unsynchronized check-then-act -> double-booking race [check]
    Hint: Check-then-act without sync -> double-booking race [check]
    Common Mistakes:
    4. In the Airline Reservation System, if the reserveSeat() method is not synchronized, which exact sequence of thread interleaving causes double-booking of the same seat?
    medium
    A. Thread 1 and Thread 2 both fail to read seat status due to visibility issues, causing booking failure.
    B. Thread 1 reads seat as available, Thread 2 reads seat as unavailable, both proceed to book.
    C. Thread 1 locks seat, Thread 2 waits; Thread 1 releases lock after booking.
    D. Thread 1 checks seat availability -> true, context switch before booking; Thread 2 checks availability -> true, both book seat.

    Solution

    1. Step 1: Identify the check-then-act race condition

      Without synchronization, Thread 1 reads seat as available but is preempted before booking; Thread 2 also reads available and proceeds.
    2. Step 2: Both threads book the same seat concurrently

      Because no lock or atomic operation protects the check-and-book sequence, double-booking occurs.
    3. Final Answer:

      Option D -> Option D
    4. Quick Check:

      Check-then-act gap without synchronization -> double-booking [check]
    Hint: Check seat available -> context switch -> both book [check]
    Common Mistakes:
    5. You need to add support for ElectricCar and ChargingSpot in the existing parking system using the Iterator pattern. What is the minimum change to the current design to support this without modifying existing classes?
    hard
    A. Modify Vehicle and Spot classes to include electric-specific fields and logic, then update iterators accordingly.
    B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map; existing classes remain unchanged.
    C. Create a new ElectricVehicle interface extending Vehicle and a ChargingSpot class, then modify existing iterators to handle them.
    D. Add a flag isElectric to Vehicle class and modify SpotCompatibility to check this flag during iteration.

    Solution

    1. Step 1: Identify extensibility requirements

      Adding ElectricCar and ChargingSpot should not require modifying existing classes to respect OCP.
    2. Step 2: Evaluate options

      Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map; existing classes remain unchanged. adds new enums and classes without modifying existing ones, preserving OCP. Modify Vehicle and Spot classes to include electric-specific fields and logic, then update iterators accordingly. modifies existing classes, violating OCP. Create a new ElectricVehicle interface extending Vehicle and a ChargingSpot class, then modify existing iterators to handle them. adds interfaces but requires modifying iterators, violating OCP. Add a flag isElectric to Vehicle class and modify SpotCompatibility to check this flag during iteration. modifies Vehicle class, violating OCP.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      Adding new enums and subclasses without changing existing classes respects OCP [check]
    Hint: Add new types via enums and subclasses, no existing class changes [check]
    Common Mistakes:
    Recommended

    Ready to prove it?

    Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

    10 questions~5 mineasy hard