Bird
Raised Fist0

Design a Ride Booking Class Hierarchy - Driver, Rider, Trip, Vehicle, Fare Calculation

LLDmediumInterview freq: high
Choose your preparation mode4 modes available
🏗️
Design a Ride Booking Class Hierarchy - Driver, Rider, Trip, Vehicle, Fare Calculation
Design a scalable, extensible ride booking system with clear domain entities and concurrency-safe trip booking
mediumInfrastructure and Utility SystemsFreq: high
Prerequisites:ConcurrencyOOP DesignSOLID PrinciplesJava Synchronization and Volatile
⚙️
Functional Requirements
  • Allow Riders to request rides with pickup and drop locations
  • Allow Drivers to register their Vehicles and update availability
  • Match Riders with nearest available Drivers and Vehicles
  • Track Trip status: REQUESTED, ACCEPTED, IN_PROGRESS, COMPLETED, CANCELLED
  • Calculate fare dynamically with support for different pricing strategies
  • Ensure thread-safe booking to prevent double assignment of Drivers or Vehicles
  • Support concurrent ride requests and driver updates
👤
Use Cases
  • Rider requests a ride specifying pickup and drop locations
  • Driver accepts or rejects a ride request
  • System matches Rider with available Driver and Vehicle
  • Trip lifecycle management: start, update location, end trip
  • Fare calculation based on distance, time, and surge pricing
  • Rider and Driver can view trip history
🧑‍💻
Actors
  • Rider
  • Driver
  • System (Ride Booking Platform)
  • Payment Gateway
❓
Clarifying Questions to Ask
  • How many concurrent ride requests and driver updates do we expect? (Concurrency gates/threads)
  • What pricing strategies should be supported? Fixed, surge, discounts?
  • Should the system support multiple vehicle types with different fare calculations?
  • How to handle edge cases like driver cancellation mid-trip or rider no-show?
  • Is the system expected to scale horizontally with distributed locking?
Out of Scope
✗ Payment processing and refunds
✗ Real-time GPS tracking and map integration
✗ User authentication and authorization
✗ Driver rating and feedback system
⚡
QPS Calculation
read qps5000
write qps2000
🖥️
Server Estimate
app servers10
db servers3
🔢
Enumerations
enum TripStatus
REQUESTED | ACCEPTED | IN_PROGRESS | COMPLETED | CANCELLED
Defines finite states of a Trip lifecycle, enabling exhaustive switch-case and preventing invalid states.
enum VehicleType
SEDAN | SUV | MOTORBIKE | VAN
Encapsulates vehicle categories to avoid string typos and supports type-specific fare calculation.
enum FareType
BASE | SURGE | DISCOUNT
Categorizes fare components for extensible fare calculation strategies.
📦
Driver
4 fields
private final String driverIdprivate String nameprivate volatile boolean isAvailableprivate Vehicle vehicle
Represents a driver with availability status and assigned vehicle. Manages availability atomically to prevent double booking.
public synchronized void setAvailability(boolean available)Atomically updates the driver's availability status.
public synchronized boolean isAvailable()Returns the current availability status safely.
public Vehicle getVehicle()Returns the vehicle assigned to the driver.
📦
Rider
3 fields
private final String riderIdprivate String nameprivate String phoneNumber
Represents a rider who can request rides and view trip history.
public Trip requestRide(Location pickup, Location drop)Creates a new Trip request with specified pickup and drop locations.
📦
Vehicle
4 fields
private final String vehicleIdprivate VehicleType typeprivate String licensePlateprivate int capacity
Represents a vehicle with type and capacity information.
public VehicleType getType()Returns the vehicle type.
public int getCapacity()Returns the seating capacity.
📦
Trip
7 fields
private final String tripIdprivate Rider riderprivate Driver driverprivate Location pickupLocationprivate Location dropLocationprivate volatile TripStatus statusprivate Fare fare
Manages the lifecycle of a ride from request to completion, ensuring thread-safe status updates.
public synchronized void updateStatus(TripStatus newStatus)Atomically updates the trip status ensuring valid state transitions.
public TripStatus getStatus()Returns the current trip status.
public synchronized boolean assignDriver(Driver driver)Assigns a driver to the trip if available, returns false if driver is unavailable.
📦
Fare
4 fields
private double baseFareprivate double surgeMultiplierprivate double discountprivate double totalFare
Calculates and stores fare details using different pricing components.
public void calculateTotalFare()Computes total fare based on base fare, surge multiplier, and discount.
public double getTotalFare()Returns the computed total fare.
📦
Location
2 fields
private double latitudeprivate double longitude
Represents geographic coordinates for pickup and drop locations.
public double distanceTo(Location other)Calculates distance to another location.
📦
FareCalculator
0 fields
Abstract class defining fare calculation behavior to support multiple pricing strategies.
public abstract double calculateFare(Trip trip)Abstract method to calculate fare for a given trip.
📦
SurgeFareCalculator
1 fields
private double surgeMultiplier
Concrete FareCalculator applying surge pricing.
public double calculateFare(Trip trip)Calculates fare applying surge multiplier.
🔗
Relationships
Driver◆──►Vehicle[1:1]
compositionVehicle lifecycle tied to Driver; Vehicle cannot exist independently
Trip──►Driver[1:1]
associationDriver exists independently; Trip references Driver
Trip──►Rider[1:1]
associationRider exists independently; Trip references Rider
🗄️
Trips
PK: tripId
tripId STRINGriderId STRINGdriverId STRINGpickupLocation STRINGdropLocation STRINGstatus STRINGfare DOUBLE
Stores trip details with partition on tripId and clustering on status for efficient queries.
🗄️
Drivers
PK: driverId
driverId STRINGname STRINGvehicleId STRINGisAvailable BOOLEAN
Stores driver info partitioned by driverId and clustered by availability for quick lookup.
🔌
POST
/requestRide
Request
{
  "riderId": "string",
  "pickupLocation": "Location",
  "dropLocation": "Location"
}
Response
{
  "tripId": "string",
  "status": "TripStatus"
}
Allows a rider to request a new ride.
🔌
POST
/assignDriver
Request
{
  "tripId": "string",
  "driverId": "string"
}
Response
{
  "success": "boolean",
  "message": "string"
}
Assigns a driver to a trip if available.
🔌
GET
/getTripStatus
Request
{
  "tripId": "string"
}
Response
{
  "status": "TripStatus"
}
Fetches the current status of a trip.

Practice

(1/5)
1. In designing the ATM system, should the Card entity be modeled as an abstract class or an interface, considering it has state (card number, expiry) and behavior (validate PIN)? Why?
easy
A. Abstract class, because Card has shared state and behavior that concrete card types inherit.
B. Interface, because Card only defines behavior and no state, allowing multiple inheritance.
C. Interface, because Java interfaces can now have default methods to hold behavior and state.
D. Abstract class, because it enforces implementation of all methods without sharing state.

Solution

  1. Step 1: Analyze Card's characteristics

    Card holds state (card number, expiry) and behavior (validate PIN), so it needs to share state and common logic.
  2. Step 2: Choose between abstract class and interface

    Abstract class supports shared state and behavior inheritance. Interface cannot hold instance state, only constants and default methods without instance fields.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Shared state + behavior -> abstract class [check]
Hint: Stateful base entity -> abstract class, not interface [check]
Common Mistakes:
2. In the Online Auction System, the AuctionManager class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is this the correct choice?
easy
A. Singleton pattern, to ensure only one AuctionManager instance manages all auctions globally.
B. Factory pattern, because it controls object creation centrally for different auction types.
C. Builder pattern, to incrementally construct complex AuctionManager configurations.
D. Prototype pattern, to clone AuctionManager instances for each auction session.

Solution

  1. Step 1: Identify Singleton intent

    The private constructor and static synchronized getInstance() method ensure only one instance of AuctionManager exists, centralizing auction state management.
  2. Step 2: Eliminate other patterns

    Factory creates multiple objects, Builder incrementally builds complex objects, Prototype clones instances--none enforce single global instance.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Private constructor + synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + static getInstance() -> Singleton [check]
Common Mistakes:
3. In designing a collection class that supports iteration, should the iterator interface be an abstract class or an interface in Java, and why?
easy
A. Interface, because it enforces immutability of the collection during iteration.
B. Abstract class, because it allows sharing state and default implementation for iteration methods.
C. Abstract class, because Java interfaces cannot have methods with parameters.
D. Interface, because it defines behavior without state, allowing multiple implementations and better flexibility.

Solution

  1. Step 1: Understand Java interface vs abstract class

    Interfaces define behavior contracts without state, allowing multiple implementations. Abstract classes can hold state but restrict multiple inheritance.
  2. Step 2: Apply to iterator design

    Iterator interface defines traversal methods (e.g., hasNext(), next()) without state, enabling different iterator implementations per collection type.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Iterator as interface enables flexible, stateless traversal contracts [check]
Hint: Iterator interface defines behavior, no state needed [check]
Common Mistakes:
4. In the Restaurant Management System, the TableManager class has a method reserveTable() that checks if a table is available and then marks it reserved. Without synchronizing reserveTable(), which exact sequence of thread interleaving causes double-booking?
medium
A. Thread 1 and Thread 2 both wait on a lock, preventing any reservation.
B. Thread 1 and Thread 2 both call reserveTable() simultaneously and both fail to reserve.
C. Thread 1 reserves a table, then Thread 2 cancels it immediately after.
D. Thread 1 checks isAvailable() -> true, context switch; Thread 2 checks isAvailable() -> true; both reserve the same table.

Solution

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

    Without synchronization, Thread 1 reads table as available but before reserving, Thread 2 also reads it as available.
  2. Step 2: Both threads proceed to reserve the same table

    Because the reservation step is not atomic, both threads mark the table reserved, causing double-booking.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Check-then-act without lock -> double-booking [check]
Hint: Check-then-act gap -> double-booking race [check]
Common Mistakes:
5. In a multi-threaded Order Management System, the Order class has a changeState() method that checks if the current state allows transition and then updates the state without synchronization. Which exact thread interleaving can cause an invalid double state transition?
medium
A. Thread 1 checks current state as PLACED -> allowed to SHIPPED, context switch before update; Thread 2 checks current state as PLACED -> allowed to CANCELLED, both update state concurrently.
B. Thread 1 and Thread 2 both call changeState() but one thread is blocked by a lock, preventing race.
C. Thread 1 reads current state as SHIPPED, Thread 2 reads as PLACED, both update to DELIVERED simultaneously.
D. Thread 1 updates state to SHIPPED, Thread 2 reads updated state and cancels order immediately.

Solution

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

    Without synchronization, both threads read the same initial state before either updates it.
  2. Step 2: Trace thread interleaving

    Thread 1 checks PLACED -> allowed to SHIPPED, context switch before update; Thread 2 also checks PLACED -> allowed to CANCELLED, both update state concurrently causing invalid double transition.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Unsynchronized check-then-act allows concurrent conflicting state updates [check]
Hint: Unsynchronized check-then-act causes double state update [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