Design a Parking Lot System - Machine Coding Interview

Designing is a parking lot is a fairly common interview question which is asked in machine coding or low level design rounds. Learn to implement a parking lot system in Java using object oriented programming principles.

Implementing a parking lot is a good problem statement to test out the object oriented programming principles of a candidate. And this is a reason why it is one of the most commonly asked interview question in machine coding or low-level design rounds.

As a candidate it is an expectation from you, to come up with a design which is scalable and efficient at handling requests coming to the system. At the same time, we should provide easy interfaces for both Customers and Administrators.

In this article, we will cover various aspects of designing a parking lot using Java, like architecture, key components and implementation details. By the end of this article, you will have a solid understanding of how to create a functional parking lot system that can be extended and customized as per specific requirements.

Table of Content

  1. Problem Statement
  2. Requirements
  3. Entities
  4. Services
  5. Commands

Parking Lot System Design

Problem Statement

Parking lot System is a software that manages the entry, exit and ticket generation for vehicles in a parking facility. It keeps a track of free parking spots in the facility and assigns it to incoming vehicles. It also generates a ticket, calculates charges, etc. This system needs to allow administartors to easily add new parking floors, parking spots in the system and track overall activity of the parking lot.

Requirements

We want to keep this implementation of parking lot management system simple. Below are some of the requirements that we will be fulfilling with out implementation.

  1. Ability to park vehicles.
  2. Get details of available spots in the parking lot.
  3. Get registration numbers of vehicles by color
  4. Get registration numbers of vehicles by type

Entities

All the core entities that we have in the system are listed below.

1. ParkingLot

The ParkingLot class manages the entire parking lot. It contains a list of parkingLot floors as a property, allowing our implementation to grow to meet the future requirements.


public class ParkingLot {
    private String id;
    private List<ParkingFloor> parkingFloors;

    public ParkingLot(String id) {
        this.id = id;
        this.parkingFloors = new ArrayList<>();
    }

    public ParkingLot(String id, List<ParkingFloor> parkingFloors) {
        this.id = id;
        this.parkingFloors = parkingFloors;
    }

    public void AddParkingFloor(ParkingFloor parkingFloor) {
        this.parkingFloors.add(parkingFloor);
    }

    public List<ParkingFloor> getParkingFloors() {
        return this.parkingFloors;
    }

    public String getId() {
        return this.id;
    }

    public ParkingSpot getAvailableSpot() {
        for(int i = 0; i < this.parkingFloors.size(); i++) {
            ParkingFloor parkingFloor = this.parkingFloors.get(i);
            ParkingSpot parkingSpot = parkingFloor.getAvailableSpot();
            if(parkingSpot != null) {
                return parkingSpot;
            }
        }

        return null;
    }
}

2. ParkingFloor

The ParkingFloor class manages the a particular floor of the parking lot. It contains a list of parking spots as a property. You can easily add new spots to the parking floor.


public class ParkingFloor {
    String id;
    private List<ParkingSpot> parkingSpots;

    public ParkingFloor(String id, int capacity) {
        this.id = id;
        this.parkingSpots = this.generateParkingSpots(capacity);
    }

    public ParkingFloor(String id, List<ParkingSpot> parkingSpots) {
        this.id = id;
        this.parkingSpots = parkingSpots;
    }

    public List<ParkingSpot> getAllParkingSpots() {
        return this.parkingSpots;
    }

    public ParkingSpot getAvailableSpot() {
        for(int i = 0; i < this.parkingSpots.size(); i++) {
            ParkingSpot parkingSpot = this.parkingSpots.get(i);

            if(parkingSpot.isAvailable()) {
                return parkingSpot;
            }
        }

        return null;
    }

    private List<ParkingSpot> generateParkingSpots(int capacity) {
        List<ParkingSpot> newParkingSpots = new ArrayList<>();

        for(int i = 0; i < capacity; i++) {
            newParkingSpots.add(new ParkingSpot(getParkingSpotId(i)));
        }

        return newParkingSpots;
    }

    private String getParkingSpotId(int idx) {
        return this.id + "-" + idx;
    }
}

3. ParkingSpot

The ParkingSpot class represents an individual parking spot at which a vehicle can be parked. These spots can be re-used between vehicles.

public class ParkingSpot {
    private String id;
    private Vehicle parkedVehicle;

    public ParkingSpot(String id) {
        this.id = id;
    }

    public boolean isAvailable() {
        return this.parkedVehicle == null;
    }

    public Ticket parkVehicle(Vehicle vehicle) {
        this.parkedVehicle = vehicle;
        return new Ticket(vehicle, this);
    }

    public void releaseVehicle() {
        this.parkedVehicle = null;
    }

    public Vehicle getParkedVehicle() {
        return this.parkedVehicle;
    }
}

4. Ticket

The Ticket class reprents a parking ticket which is given to the customer when they have successfully parked the car. Customer can return the ticket on their way back to authenticate themselves as the owner of the Vehicle.

public class Ticket {
    private String id;
    private Vehicle vehicle;
    private ParkingSpot parkingSpot;
    private LocalDateTime parkedAt;

    public Ticket(Vehicle vehicle, ParkingSpot parkingSpot) {
        this.id = UUID.randomUUID().toString();
        this.parkedAt = LocalDateTime.now();
        this.vehicle = vehicle;
        this.parkingSpot = parkingSpot;
    }

    public ParkingSpot getParkingSpot() {
        return this.parkingSpot;
    }

    public LocalDateTime getParkedAt() {
        return this.parkedAt;
    }
}

5. Vehicle

The Vehicle class represents the vehicle being parked in the parking spot. A vehicle can have multiple properties like color, registration number, type, etc.

public class Vehicle {
    private String registrationNumber;
    private VehicleType type;
    private String color;

    public Vehicle(String registrationNumber, VehicleType type, String color) {
        this.registrationNumber = registrationNumber;
        this.type = type;
        this.color = color;
    }

    public String getRegistrationNumber() {
        return this.registrationNumber;
    }

    public String getColor() {
        return this.color;
    }

    public VehicleType getType() {
        return this.type;
    }
}

Once, we have created all these basic entities, lets start stiching them together using services. Below are Services, that we will have in the parking lot system.

Services

1. ParkingLotService

The class ParkingLotService is the main orchestrating class. It manages all the fucntionalities being offered by the parking lot system.

public interface ParkingLotService {
    void addParkingFloor(int capacity);
    Ticket parkVehicle(Vehicle vehicle);
    double releaseSpace(Ticket ticket);
}
public class ParkingLotServiceImpl implements ParkingLotService {

    private ParkingLot parkingLot;
    private PricingStrategy pricingStrategy;

    public ParkingLotServiceImpl(ParkingLot parkingLot, PricingStrategy pricingStrategy) {
        this.parkingLot = parkingLot;
        this.pricingStrategy = pricingStrategy;
    }

    @Override
    public ParkingLot getParkingLot() {
        return this.parkingLot;
    }


    @Override
    public void addParkingFloor(int capacity) {
        String newFloorId = this.getNewFloorId();
        this.parkingLot.AddParkingFloor(new ParkingFloor(newFloorId, capacity));
    }

    @Override
    public Ticket parkVehicle(Vehicle vehicle) {
        ParkingSpot spot = this.parkingLot.getAvailableSpot();
        if(spot == null) {
            throw new NoAvailableSpotException();
        }

        return spot.parkVehicle(vehicle);
    }

    @Override
    public double releaseSpace(Ticket ticket) {
        ticket.getParkingSpot().releaseVehicle();
        return this.pricingStrategy.calculateParkingFee(ticket);
    }

    private String getNewFloorId() {
        int floorIdx = this.parkingLot.getParkingFloors().size() + 1;
        return this.parkingLot.getId() + "-" + floorIdx;
    }
}

2. PricingStrategy

The class PricingStrategy defines how pricing would be calculated when we release a vehicle from the parking spot. You can have multiple pricing strategies in the system. We currently have only, perMinute based startegy.

public interface PricingStrategy {
    double calculateParkingFee(Ticket ticket);
}
public class PerMinutePricingStrategy implements PricingStrategy {

    private int perMinuteRate;

    public PerMinutePricingStrategy(int perMinuteRate) {
        this.perMinuteRate = perMinuteRate;
    }

    public double calculateParkingFee(Ticket ticket) {
        LocalDateTime exitTime = LocalDateTime.now();
        long parkedTimeInMins = Duration.between(ticket.getParkedAt(), exitTime).toMinutes();
        return parkedTimeInMins * this.perMinuteRate;
    }
}

Commands

Command Interface

We wanted to create a parking management system, that is extensible. Therefore, we are proceeding with a command based architecture. As an implemenetor, you dont have to bake all functioinalities in the entities and services created above. You can easily add new commands to the system which leverages the functionalities provided by entites and services.

public interface Command {
    void validate();
    void execute(ParkingLotService service);
}

1. Park Vehicle Command

This command allow you to park the vehicle in the first available parking spot. If there are no parking spots available then an exception would be throws.

public class ParkVehicleCommand implements Command {

    String[] args;

    public ParkVehicleCommand(String args) {
        this.args = args.split(",");
    }

    @Override
    public void validate() {
        if(this.args.length != 3) {
            throw new InvalidCommandException();
        }
    }

    @Override
    public void execute(ParkingLotService service) {
        Vehicle vehicle = new Vehicle(args[0], VehicleType.valueOf(args[1]), args[2]);
        service.parkVehicle(vehicle);
    }
}

2. Get Vehicle By Color Command

This command prints all the parked vehicles of a particular color.

public class VehicleByColorCommand implements Command {
    String color;

    public VehicleByColorCommand(String color) {
        this.color = color;
    }

    @Override
    public void validate() {
        if(this.color.isEmpty() || this.color.isBlank()) {
            throw new InvalidCommandException();
        }
    }

    @Override
    public void execute(ParkingLotService service) {
        for(int i = 0; i < service.getParkingLot().getParkingFloors().size(); i++) {
            ParkingFloor floor = service.getParkingLot().getParkingFloors().get(i);

            for(int j = 0; j < floor.getAllParkingSpots().size(); j++) {
                Vehicle parkedVehicle = floor.getAllParkingSpots().get(j).getParkedVehicle();

                if(parkedVehicle != null && parkedVehicle.getColor().equals(this.color)) {
                    System.out.println(parkedVehicle.getRegistrationNumber());
                }
            }
        }
    }
}

3. Get Vehicle By Type Command

This command prints all the parked vehicles of a particular type.

public class VehicleByTypeCommand implements Command {
    VehicleType type;

    public VehicleByTypeCommand(String type) {
        this.type = VehicleType.valueOf(type);
    }

    @Override
    public void validate() {
    }

    @Override
    public void execute(ParkingLotService service) {
        for(int i = 0; i < service.getParkingLot().getParkingFloors().size(); i++) {
            ParkingFloor floor = service.getParkingLot().getParkingFloors().get(i);

            for(int j = 0; j < floor.getAllParkingSpots().size(); j++) {
                Vehicle parkedVehicle = floor.getAllParkingSpots().get(j).getParkedVehicle();

                if(parkedVehicle != null &&  parkedVehicle.getType() == this.type) {
                    System.out.println(parkedVehicle.getRegistrationNumber());
                }
            }
        }
    }
}

So, this is the implementation of the Parking System Design. You can find the complete source code here.