Full Stack • Java • System Design • Cloud • AI Engineering

System Design2024-01-15

Design a Parking Lot System

Learn how to design a parking lot system with object-oriented principles, handling multiple vehicle types, parking spots, and payment processing.

Design a Parking Lot System

Problem Statement

Design a parking lot system that can:

  • Handle multiple floors
  • Support different vehicle types (Car, Motorcycle, Truck)
  • Track available spots
  • Calculate parking fees
  • Process payments

Requirements

Functional Requirements

  1. Multiple entry and exit points
  2. Different parking spot sizes (Compact, Large, Handicapped)
  3. Real-time availability tracking
  4. Automated payment system
  5. Ticket generation and validation

Non-Functional Requirements

  1. High availability
  2. Low latency for spot assignment
  3. Scalable to multiple locations
  4. Secure payment processing

Class Diagram

ParkingLot
├── floors: List<Floor>
├── entrances: List<Entrance>
├── exits: List<Exit>
└── paymentProcessor: PaymentProcessor

Floor
├── spots: List<ParkingSpot>
└── availableSpots: Map<SpotType, Integer>

ParkingSpot (Abstract)
├── CompactSpot
├── LargeSpot
└── HandicappedSpot

Vehicle (Abstract)
├── Car
├── Motorcycle
└── Truck

Ticket
├── ticketId: String
├── vehicle: Vehicle
├── spot: ParkingSpot
├── entryTime: DateTime
└── exitTime: DateTime

Implementation

Core Classes

public enum VehicleType {
    CAR, MOTORCYCLE, TRUCK
}

public enum SpotType {
    COMPACT, LARGE, HANDICAPPED
}

public abstract class Vehicle {
    private String licensePlate;
    private VehicleType type;
    
    public abstract boolean canFitInSpot(ParkingSpot spot);
}

public class Car extends Vehicle {
    public Car(String licensePlate) {
        super(licensePlate, VehicleType.CAR);
    }
    
    @Override
    public boolean canFitInSpot(ParkingSpot spot) {
        return spot.getType() == SpotType.COMPACT || 
               spot.getType() == SpotType.LARGE;
    }
}

public abstract class ParkingSpot {
    private String spotId;
    private SpotType type;
    private boolean isAvailable;
    private Vehicle currentVehicle;
    
    public boolean park(Vehicle vehicle) {
        if (!isAvailable || !vehicle.canFitInSpot(this)) {
            return false;
        }
        this.currentVehicle = vehicle;
        this.isAvailable = false;
        return true;
    }
    
    public void removeVehicle() {
        this.currentVehicle = null;
        this.isAvailable = true;
    }
}

public class ParkingLot {
    private static ParkingLot instance;
    private List<Floor> floors;
    private Map<String, Ticket> activeTickets;
    
    private ParkingLot() {
        floors = new ArrayList<>();
        activeTickets = new HashMap<>();
    }
    
    public static synchronized ParkingLot getInstance() {
        if (instance == null) {
            instance = new ParkingLot();
        }
        return instance;
    }
    
    public Ticket issueTicket(Vehicle vehicle) {
        ParkingSpot spot = findAvailableSpot(vehicle);
        if (spot == null) {
            throw new NoAvailableSpotException();
        }
        
        spot.park(vehicle);
        Ticket ticket = new Ticket(vehicle, spot);
        activeTickets.put(ticket.getId(), ticket);
        return ticket;
    }
    
    private ParkingSpot findAvailableSpot(Vehicle vehicle) {
        for (Floor floor : floors) {
            ParkingSpot spot = floor.findAvailableSpot(vehicle);
            if (spot != null) {
                return spot;
            }
        }
        return null;
    }
    
    public double calculateFee(Ticket ticket) {
        long hours = ticket.getDurationInHours();
        return hours * 5.0; // $5 per hour
    }
}

Design Patterns Used

1. Singleton Pattern

  • ParkingLot uses Singleton to ensure single instance

2. Factory Pattern

  • VehicleFactory to create different vehicle types

3. Strategy Pattern

  • Different pricing strategies (hourly, daily, monthly)

4. Observer Pattern

  • Notify when spots become available

Interview Questions

Q1: How would you handle peak hours?

Answer:

  • Implement reservation system
  • Dynamic pricing during peak hours
  • Queue management for waiting vehicles
  • Real-time availability updates

Q2: How to scale to multiple locations?

Answer:

  • Separate database per location
  • Central management system
  • Location-based routing
  • Distributed caching (Redis)

Q3: How to handle payment failures?

Answer:

  • Retry mechanism with exponential backoff
  • Multiple payment methods
  • Grace period for payment
  • Manual payment option at exit

Optimization Strategies

  1. Caching: Cache available spots count per floor
  2. Indexing: Index on vehicle license plate and ticket ID
  3. Load Balancing: Distribute entry/exit processing
  4. Async Processing: Process payments asynchronously

Conclusion

This parking lot design demonstrates:

  • Object-oriented design principles
  • Design pattern application
  • Scalability considerations
  • Real-world problem solving

Key takeaways:

  • Use appropriate design patterns
  • Consider edge cases
  • Plan for scalability
  • Handle failures gracefully