- Introduction
- Features
- Design Principles & Patterns
- Project Structure
- How to Compile & Run
- Known Limitations & Future Enhancements
- Authors & Acknowledgements
Zuber is a simplified ride-sharing application designed as a Low-Level Design (LLD) hackathon project. Its core purpose is to demonstrate robust software architecture, design patterns, and adherence to object-oriented programming principles in building a scalable and maintainable system. The application addresses the fundamental problem of connecting riders with available drivers for on-demand transportation, incorporating features like driver management, ride booking, dynamic fare calculation, and flexible payment processing.
The Zuber application provides the following core functionalities:
- Driver Management: Allows for the addition of new drivers and listing of currently available vehicles/drivers.
- Ride Request & Acceptance: Riders can request a ride specifying their preferred vehicle type, and the system attempts to find a matching driver who then "accepts" the ride. As of now Ride is always accepted.
- Dynamic Fare Calculation: Computes ride fares based on a base charge and distance, with the ability to apply discounts and surcharges dynamically.
- Flexible Payment Processing: Supports various payment methods (e.g., Credit Card, UPI) through an extensible payment system.
- Notifications: Provides basic notifications to riders (e.g., ride status, driver arrival) and drivers (e.g., new ride requests, payment received).
- Ride Creation: Supports different ride categories such as Bike, SUV, Sedan managed by a factory.
- Zuber: It is a orchestartor class which binds the whole application together.
The primary goal of this project was to create extensible and maintainable codebase adhering to key Low-Level Design (LLD) principles and demonstrating the effective use of design patterns.
- Singleton Pattern:
- Applied to:
DriverManagerandRiderManager - Why: Ensures that there is only one instance of the
DriverManagerandRiderManagerresponsible for managing all drivers and Riders respectively throughout the application's lifecycle, providing a single point of access and preventing inconsistent states.
- Applied to:
- Decorator Pattern:
- Applied to:
FareDecoratorhierarchy (Fare,Discount,SurplusCharge) - Why: Dynamically adds responsibilities (discounts, surcharges) to the base fare calculation (
Fare) without altering its core structure. This promotes adherence to the Open/Closed Principle, allowing new fare adjustments to be introduced easily. There is seperate class for Fare calculation which calculates the fare and gives back to Decorator class.
- Applied to:
- Strategy Pattern:
- Applied to:
PaymentStrategyhierarchy (UpiPaymentStrategy,CreditCardPaymentStrategy)BookRideStrategyhierarchy (BookRideByVehicleStrategy)
- Why: Defines a family of algorithms (different payment methods, different ride booking approaches), encapsulates each one, and makes them interchangeable at runtime. This allows new payment or booking strategies to be added without modifying the client code.
- Applied to:
- Factory Design Pattern:
- Applied to:
RideFactory - Why: Provides an interface for creating objects (
Ridetypes likeBikeRide,SuvRide, etc.) without specifying their concrete classes. This decouples the client (BookRideByVehicleStrategy) from the specific implementation details of theRideobjects being created.
- Applied to:
- Single Responsibility Principle (SRP): Each class is designed to have a single, well-defined responsibility. For example,
NotificationServicefocuses solely on sending notifications, andPaymentStrategyhandles only payment processing. - Open/Closed Principle (OCP): Illustrated effectively by the Decorator and Strategy patterns, allowing new functionalities (e.g., a new fare type, a new payment gateway, a new booking algorithm) to be introduced by adding new classes without modifying existing, tested code.
- Encapsulation: Data members are generally kept private (
private) and accessed via public getter methods, controlling external access and maintaining data integrity. - Modularity & Separation of Concerns: The project is divided into logical modules (
models,services,managers,strategies,factories,decorator), ensuring clear boundaries and reducing inter-module dependencies.
Use a C++ compiler
execute the main.cpp file
There is one rider already provided.
Create a new rider with available vehicle options - suv ,sedan, bike, carpool.
Available payment options are CreditCard and UPI.
While demonstrating core LLD principles, this project has certain limitations and areas for future enhancement:
-
Memory Management: The current implementation relies heavily on raw pointers (new and delete). Manual memory management for dynamically allocated objects (e.g., Driver, Rider, BookRideStrategy, PaymentStrategy, RideFactory, Ride) is not fully robust, leading to potential memory leaks if delete calls are missed.
-
Limited Location: There are only 26 locations taken from 'a' to 'z' and for fare calculations hard coded the logic for each type. So further upgradability requires some modification in CalculateFare.
-
Total Fare Calculation: The Fare Calculation are hard coded for simplicity.
- More sophisticated driver matching (e.g., by location, real-time availability)with the help of
BookRideStrategy - Ride cancellation and dispute resolution.
- Adding More Type of Vehicles.
- Real-time ride updates with also Schedule Ride for the future time
- A more interactive command-line interface or a graphical user interface.
Author: Rahul Raman
Development Environment: C++17, g++
Special Thanks: To the mentors Rohit Negi and Aditya Tandon for the LLD Hackathon for providing this valuable learning opportunity.