Design Cab Booking Service
Design a simplified cab booking service in which passengers can request and cancel rides, while drivers can accept and complete them. Each ride contains a supplied ride ID, pickup location, destination, ride type, estimated distance, estimated travel time, and calculated fare.
All passenger IDs, driver IDs, and ride IDs are supplied as input. The service must not generate any identifiers.
Constructor
CabBookingService(List<String> passengerIds, List<String> driverIds)
- Initializes the service with all existing passenger and driver IDs.
- Every passenger ID and driver ID is supplied as input.
- Every driver is initially available.
Methods
Request Ride
String requestRide(String rideId, String passengerId, String pickupLocation, String destination, double distanceKm, String rideType, int rideTimeMinutes)
- Creates a new ride using the supplied
rideId.
- The service does not generate the ride ID.
- The supplied passenger must not currently have another active ride.
- The new ride has status
REQUESTED.
- The new ride initially has no assigned driver.
- Calculates the fare using the distance, ride type, and estimated travel time.
- The calculated fare is truncated to an integer.
- Returns the created ride in the specified ride format.
Accept Ride
String acceptRide(String driverId, String rideId)
- Assigns the supplied available driver to the requested ride.
- The ride must currently have status
REQUESTED.
- The supplied driver must currently be available.
- Changes the ride status from
REQUESTED to ACCEPTED.
- The assigned driver becomes unavailable until the ride is completed or cancelled.
- Returns the updated ride.
Complete Ride
String completeRide(String driverId, String rideId)
- Completes a ride previously accepted by the supplied driver.
- The ride must currently have status
ACCEPTED.
- The supplied
driverId must be the driver assigned to the ride.
- Changes the ride status from
ACCEPTED to COMPLETED.
- The driver becomes available again.
- Returns the updated ride.
Cancel Ride
String cancelRide(String passengerId, String rideId)
- Cancels a ride belonging to the supplied passenger.
- The supplied
passengerId must be the passenger who requested the ride.
- A ride may be cancelled while its status is
REQUESTED or ACCEPTED.
- If an accepted ride is cancelled, its assigned driver becomes available again.
- Changes the ride status to
CANCELLED.
- Returns the updated ride.
Ride Format
Every returned ride is represented as:
"rideId,status,passengerId,driverId,pickupLocation,destination,rideType,fare"
status is REQUESTED, ACCEPTED, COMPLETED, or CANCELLED.
driverId is none until a driver accepts the ride.
fare is represented as an integer.
fare does not contain a decimal point.
- A cancelled ride retains its assigned driver ID if it had already been accepted.
Fare Calculation
The fare before truncation is calculated as:
baseFare + distanceKm × distanceRate + rideTimeMinutes × timeRate
STANDARD: base fare = 40.0, distance rate = 12.0 per kilometre, time rate = 2.0 per minute.
PREMIUM: base fare = 80.0, distance rate = 20.0 per kilometre, time rate = 3.0 per minute.
- The complete fare expression is evaluated before truncation.
- The final fare is truncated to an integer by removing the fractional part.
- The fare is not rounded to the nearest integer.
- For example, a calculated fare of
42.75 becomes 42.
- Because all fare values are positive, truncation is equivalent to rounding down.
Ride Lifecycle
REQUESTED → ACCEPTED → COMPLETED
REQUESTED → CANCELLED
REQUESTED → ACCEPTED → CANCELLED
- A ride with status
COMPLETED or CANCELLED cannot change status again.
Active Ride
- A ride is active when its status is
REQUESTED or ACCEPTED.
- A ride with status
COMPLETED or CANCELLED is no longer active.
- A passenger cannot request another ride while the passenger already has an active ride.
- After a passenger's active ride is completed or cancelled, the passenger may request another ride.
Identifier Rules
- All passenger IDs and driver IDs are supplied to the constructor.
- Every ride ID is supplied to
requestRide.
- The service does not generate passenger IDs, driver IDs, or ride IDs.
- Each passenger ID is unique.
- Each driver ID is unique.
- Each ride ID is unique across all requested rides.
- A ride ID cannot be reused after its ride is completed or cancelled.
Constraints
1 ≤ passengerIds.size() ≤ 10,000
1 ≤ driverIds.size() ≤ 10,000
passengerIds contains no duplicate values.
driverIds contains no duplicate values.
1 ≤ rideId.length(), passengerId.length(), driverId.length() ≤ 100
1 ≤ pickupLocation.length(), destination.length() ≤ 100
0.1 ≤ distanceKm ≤ 10,000.0
1 ≤ rideTimeMinutes ≤ 10,000
rideType is either STANDARD or PREMIUM.
- IDs contain only lowercase English letters, digits, and hyphens.
- Locations do not contain commas.
- No parameter value is
null.
- Every
passengerId passed to a method exists in passengerIds.
- Every
driverId passed to a method exists in driverIds.
- Every
rideId passed to requestRide is unique.
- Every
rideId passed to another method refers to an existing ride.
- Every method call satisfies the required ride status, passenger ownership, driver assignment, and driver availability conditions.
- A passenger has at most one active ride at a time.
- A driver accepts at most one active ride at a time.
Examples
Parameter names are shown in method calls only for readability.
Example 1: Request, Accept, and Complete a Standard Ride
CabBookingService service = new CabBookingService(passengerIds = List.of("passenger-7", "passenger-9"), driverIds = List.of("driver-2", "driver-5"));
service.requestRide(rideId = "booking-101", passengerId = "passenger-7", pickupLocation = "airport", destination = "city-center", distanceKm = 8.5, rideType = "STANDARD", rideTimeMinutes = 24)
Output: "booking-101,REQUESTED,passenger-7,none,airport,city-center,STANDARD,190"
The fare before truncation is 40.0 + 8.5 × 12.0 + 24 × 2.0 = 190.0. The returned integer fare is 190.
service.acceptRide(driverId = "driver-2", rideId = "booking-101")
Output: "booking-101,ACCEPTED,passenger-7,driver-2,airport,city-center,STANDARD,190"
service.completeRide(driverId = "driver-2", rideId = "booking-101")
Output: "booking-101,COMPLETED,passenger-7,driver-2,airport,city-center,STANDARD,190"
Example 2: Cancel a Premium Ride Before Acceptance
service.requestRide(rideId = "booking-205", passengerId = "passenger-9", pickupLocation = "railway-station", destination = "technology-park", distanceKm = 12.0, rideType = "PREMIUM", rideTimeMinutes = 30)
Output: "booking-205,REQUESTED,passenger-9,none,railway-station,technology-park,PREMIUM,410"
The fare before truncation is 80.0 + 12.0 × 20.0 + 30 × 3.0 = 410.0. The returned integer fare is 410.
service.cancelRide(passengerId = "passenger-9", rideId = "booking-205")
Output: "booking-205,CANCELLED,passenger-9,none,railway-station,technology-park,PREMIUM,410"
Example 3: Cancel an Accepted Ride with a Truncated Fare
service.requestRide(rideId = "trip-88", passengerId = "passenger-7", pickupLocation = "museum", destination = "university", distanceKm = 5.27, rideType = "STANDARD", rideTimeMinutes = 15)
Output: "trip-88,REQUESTED,passenger-7,none,museum,university,STANDARD,133"
The fare before truncation is 40.0 + 5.27 × 12.0 + 15 × 2.0 = 133.24. The fractional part is removed, so the returned fare is 133.
service.acceptRide(driverId = "driver-5", rideId = "trip-88")
Output: "trip-88,ACCEPTED,passenger-7,driver-5,museum,university,STANDARD,133"
service.cancelRide(passengerId = "passenger-7", rideId = "trip-88")
Output: "trip-88,CANCELLED,passenger-7,driver-5,museum,university,STANDARD,133"
Example 4: Fractional Fare Is Truncated
service.requestRide(rideId = "trip-99", passengerId = "passenger-9", pickupLocation = "market", destination = "library", distanceKm = 4.23, rideType = "STANDARD", rideTimeMinutes = 8)
Output: "trip-99,REQUESTED,passenger-9,none,market,library,STANDARD,106"
The fare before truncation is 40.0 + 4.23 × 12.0 + 8 × 2.0 = 106.76. The fractional part is removed, so the returned fare is 106.