Bird
Raised Fist0

Food Ordering System

LLDmediumInterview freq: high
Choose your preparation mode4 modes available
๐ŸŽฏ
Design a Food Ordering System
40 min ยท medium
medium

Design a food ordering system that supports a menu with multiple categories and items, allows customers to add items to a cart, place orders, manages a kitchen queue for order preparation, and handles delivery assignments.

  • Menu consists of categories and items with prices and availability status.
  • Cart holds multiple menu items with quantities and calculates total price.
  • Order lifecycle includes states: PLACED, PREPARING, READY, DELIVERING, DELIVERED.
  • Kitchen queue processes orders in FIFO manner with concurrency control.
  • Delivery assigns orders to delivery agents with availability tracking.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Design Menu and MenuItem classes
Design classes to represent the Menu and MenuItem entities, including categories, prices, and availability.
๐Ÿ’ก Hint
Use enum for category and availability status to enforce type safety and exhaustive switch-case handling.
Use enum Category and enum AvailabilityStatus for MenuItem fields - enums provide compile-time safety and prevent runtime typos unlike Strings.
Mid: Uses enum for categories and availability. Senior: Explains how enum prevents invalid category values at compile time and enables exhaustive switch-case checks.
โš ๏ธCommon Wrong Answer
Using String for category and availability leads to runtime bugs due to typos and no IDE support for valid values.
Step 2
Design Cart with thread-safe item addition
Design a Cart class that holds multiple MenuItems with quantities and supports concurrent additions and removals.
๐Ÿ’ก Hint
Use synchronized methods or ConcurrentHashMap to ensure atomic updates to item quantities and prevent race conditions.
public synchronized void addItem(MenuItem item, int quantity) - synchronized to atomically update item quantities and prevent lost updates in concurrent threads.
Mid: Uses synchronized methods for thread safety. Senior: Traces race condition where two threads concurrently add the same item causing lost increments without synchronization.
โš ๏ธCommon Wrong Answer
Not synchronizing addItem leads to race conditions where item quantities are incorrectly updated, causing inconsistent cart state.
Step 3
Design Order class with state management
Design an Order class that tracks order items, total price, and order state transitions with thread-safe state updates.
๐Ÿ’ก Hint
Use enum OrderState and volatile field for current state to ensure visibility across threads; synchronize state transition methods to enforce valid transitions.
volatile OrderState currentState; synchronized boolean updateState(OrderState newState) - volatile ensures visibility, synchronized enforces atomic valid state transitions.
Mid: Uses enum for states and synchronized update method. Senior: Explains how volatile guarantees visibility of state changes across threads and synchronized prevents invalid concurrent transitions.
โš ๏ธCommon Wrong Answer
Using non-volatile, non-synchronized state field causes stale reads and invalid concurrent state transitions leading to inconsistent order lifecycle.
Step 4
Design KitchenQueue with thread-safe order processing
Design a KitchenQueue class that manages orders in FIFO order and supports concurrent order addition and processing.
๐Ÿ’ก Hint
Use a thread-safe queue like ConcurrentLinkedQueue or synchronize methods that modify the queue to prevent race conditions.
private final ConcurrentLinkedQueue<Order> orderQueue = new ConcurrentLinkedQueue<>(); public boolean addOrder(Order o) { return orderQueue.offer(o); } - ConcurrentLinkedQueue provides lock-free thread-safe FIFO queue.
Mid: Uses thread-safe queue for order management. Senior: Explains how ConcurrentLinkedQueue avoids blocking and race conditions compared to manual synchronization.
โš ๏ธCommon Wrong Answer
Using non-thread-safe queue like LinkedList without synchronization causes race conditions and lost or duplicated orders.
Step 5
Design Delivery assignment with agent availability
Design DeliveryAgent and DeliveryManager classes to assign orders to available agents with concurrency control.
๐Ÿ’ก Hint
Use volatile boolean isAvailable in DeliveryAgent and synchronized method assignOrder(Order) in DeliveryManager to atomically check and assign orders.
volatile boolean isAvailable in DeliveryAgent; public synchronized boolean assignOrder(Order o) - synchronized to atomically check isAvailable and assign order preventing double assignment.
Mid: Uses volatile for availability and synchronized assign method. Senior: Traces thread interleaving where without synchronization two threads assign the same agent causing double delivery.
โš ๏ธCommon Wrong Answer
Not using volatile and synchronized leads to visibility issues and race conditions causing multiple orders assigned to the same delivery agent.
๐Ÿ”Œ
API Design Challenge
Design RESTful APIs to support menu browsing, cart management, order placement, kitchen queue monitoring, and delivery tracking.
  • Use resource-oriented endpoints like /menu, /cart, /orders, /kitchen-queue, /delivery-agents.
  • Support concurrency by designing idempotent POST/PUT methods and optimistic locking where needed.
  • Include pagination and filtering for menu and order listing APIs to handle large data sets.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • Use enum for fixed categories and states to prevent runtime bugs
  • Synchronized methods for atomic updates to Cart and Order state
  • Volatile fields for visibility of shared mutable state
  • Thread-safe queue (ConcurrentLinkedQueue) for KitchenQueue
  • Synchronized assignment method with volatile availability flag in DeliveryAgent
Good to Have - 30%
  • Clear separation of concerns following Single Responsibility Principle
  • Composition relationships with lifecycle ownership explained
  • Explicit state transition validation in Order class
  • Use of concurrent collections to avoid explicit locking where possible
Bonus - 10%
  • Detailed concurrency scenario tracing for race conditions
  • Use of immutable objects or defensive copies for Cart snapshot in Order
  • Extensibility considerations following Open/Closed Principle
Using String for categories or states leads to runtime typos and no compile-time checks, causing bugs that are hard to detect and fix.
Not synchronizing shared mutable state causes race conditions leading to inconsistent data and unpredictable behavior.
Using non-thread-safe collections like LinkedList without synchronization causes lost or duplicated orders under concurrent access.
Not using volatile for shared flags causes stale reads and visibility issues, resulting in incorrect concurrency behavior.
Assigning orders to delivery agents without synchronization can cause double assignments due to race conditions.
โœ…
Model Answer
โ–ผ Reveal

This design models a food ordering system with clear separation of concerns and Java concurrency best practices. Enums enforce type safety for categories and states. Synchronized methods and volatile fields ensure thread-safe updates. ConcurrentLinkedQueue provides efficient FIFO order processing. Delivery assignment uses volatile flags and synchronized methods to prevent race conditions.

  • Use enum types for fixed categories and availability to prevent runtime errors.
  • Synchronize Cart methods to atomically update item quantities and prevent lost updates.
  • Declare OrderState as volatile and synchronize state transition methods to ensure visibility and atomicity.
  • Use ConcurrentLinkedQueue for KitchenQueue to avoid explicit locking and improve throughput.
  • Use volatile boolean isAvailable in DeliveryAgent and synchronize assignOrder in DeliveryManager to prevent double assignment.
  • Use enum types for fixed categories and states to enforce compile-time safety and exhaustive switch-case handling.
  • Use synchronized methods to atomically update shared mutable state such as Cart items and Order state transitions.
  • Use volatile fields for state variables accessed by multiple threads to guarantee visibility of changes.
  • Use thread-safe concurrent collections like ConcurrentLinkedQueue for KitchenQueue to avoid explicit locking and improve throughput.
  • Use synchronized methods combined with volatile flags for DeliveryAgent availability to prevent race conditions in order assignment.
  • Design Order lifecycle with explicit states and synchronized transitions to enforce valid state changes and prevent invalid concurrent updates.
  • Separate concerns by modeling Menu, Cart, Order, KitchenQueue, and DeliveryManager as distinct classes with clear responsibilities (Single Responsibility Principle).
  • Use composition relationships for Cart containing MenuItems and Order containing Cart snapshot to maintain lifecycle ownership.
  • Use inheritance or realization only if common behavior with shared state exists; otherwise prefer composition to keep design flexible (favor composition over inheritance).
  • Ensure all concurrency-sensitive methods have explicit Java concurrency constructs named (volatile, synchronized, concurrent collections) to prevent subtle bugs.

Practice

(1/5)
1. A class CarBuilder constructs a Car object step-by-step using methods like setEngine(), setWheels(), and finally build(). The Car constructor is private. Which design pattern is applied here and why is it the correct choice?
easy
A. Builder pattern, because it separates complex object construction from its representation, allowing stepwise creation.
B. Factory Method pattern, because it creates objects through inheritance and subclasses.
C. Prototype pattern, because it clones existing objects to create new ones efficiently.
D. Singleton pattern, because it ensures only one instance of Car exists.

Solution

  1. Step 1: Identify the construction process

    The class uses multiple setter methods to configure parts of the object before finalizing with build(), indicating stepwise construction.
  2. Step 2: Match pattern intent

    Builder pattern separates construction from representation, ideal for complex objects with many optional parts. Factory Method uses inheritance, Prototype clones objects, Singleton restricts instance count.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Stepwise object creation with private constructor -> Builder pattern [check]
Hint: Stepwise construction with private constructor -> Builder pattern [check]
Common Mistakes:
2. In the Elevator System, should the Elevator class implement an interface or extend an abstract class to represent different elevator types (e.g., passenger, freight)? Consider that elevators share common state and behavior but also have type-specific logic.
easy
A. Use an abstract class, because elevators share common state and partial behavior implementations.
B. Use an interface, because elevators only share behavior signatures, not state.
C. Use a concrete class, because inheritance is unnecessary for elevator types.
D. Use multiple interfaces, one per behavior, to avoid state duplication.

Solution

  1. Step 1: Analyze shared state and behavior

    Elevators share common fields like currentFloor, direction, and doorStatus, plus some implemented methods.
  2. Step 2: Abstract class fits shared state and partial behavior

    Abstract class allows code reuse and enforces common state, while subclasses override type-specific logic.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class supports shared state and behavior [check]
Hint: Abstract class for shared state; interface for pure behavior [check]
Common Mistakes:
3. In a UML class diagram, should Vehicle be modeled as an abstract class or an interface if it has state like speed and behavior like accelerate()? Why?
easy
A. Interface, because it allows multiple inheritance and defines behavior without state.
B. Abstract class, because it can hold shared state and provide default behavior implementations.
C. Interface, because state can be stored externally and behavior defined via default methods.
D. Abstract class, because interfaces cannot declare methods with parameters.

Solution

  1. Step 1: Analyze state and behavior requirements

    Vehicle has state (speed) and behavior (accelerate()), so it needs a construct that supports instance variables and method implementations.
  2. Step 2: Evaluate abstract class vs interface

    Abstract classes can hold state and provide default behavior; interfaces cannot hold instance state (only constants) and default methods can't maintain state.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    State + behavior with shared code -> abstract class [check]
Hint: Stateful base class -> abstract class, not interface [check]
Common Mistakes:
4. The Car Rental System currently uses a FeeStrategy interface for pricing. To add a WeekendFeeStrategy without modifying existing classes, which SOLID principle is being applied, and what is the minimum required change?
medium
A. Single Responsibility Principle; modify the existing FeeStrategy implementations to include weekend logic.
B. Open/Closed Principle; create a new WeekendFeeStrategy class implementing FeeStrategy without changing existing code.
C. Liskov Substitution Principle; override existing FeeStrategy methods to handle weekend fees.
D. Dependency Inversion Principle; inject WeekendFeeStrategy into existing classes by modifying their constructors.

Solution

  1. Step 1: Identify the SOLID principle for extensibility

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: Determine minimal change to add WeekendFeeStrategy

    To avoid modifying existing classes, inject the new WeekendFeeStrategy via dependency injection, applying Dependency Inversion Principle (DIP) to depend on abstractions.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Inject new strategy without modifying existing classes -> DIP [check]
Hint: Add new strategy via injection, not modifying existing code [check]
Common Mistakes:
5. To support ElectricCar and ChargingSpot in the Wallet System's parking module, what is the minimum change to the current design to maintain extensibility without modifying existing classes?
hard
A. Modify Vehicle base class to include isElectric flag and add ChargingSpot as a subclass of Spot.
B. Create a new ElectricVehicle interface and modify existing Vehicle and Spot classes to implement it.
C. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally.
D. Add logic for ElectricCar and ChargingSpot directly inside existing Vehicle and Spot classes.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Adding new enum values and subclasses externally allows extension without changing existing code, preserving OCP.
  2. Step 2: Use external compatibility map update

    Updating SpotCompatibility.COMPATIBILITY_MAP externally avoids modifying existing classes, enabling new vehicle and spot types to integrate cleanly.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    New enums + subclass + external map update -> no existing class changes [check]
Hint: Add enums + subclass + external map update -> no existing class change [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