Parking Lot Design | workattech | Review Appreciated

Problem Statement

Driver.java

package ParkingLot;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Driver {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] str = br.readLine().split(" ");
        ParkingLot parkingLot = new ParkingLot(str[1], str[2], str[3]);
        parkingLot.initializeParkingLot();

        outerloop:
        while(true){
            String[] cmd = br.readLine().split(" ");
            switch (cmd[0]){
                case "create_parking_lot":
                    break;
                case "park_vehicle":
                    Vehicle vehicle = new Vehicle(cmd[1], cmd[2], cmd[3]);
                    parkingLot.parkVehicle(vehicle);
                    break;
                case "unpark_vehicle":
                    parkingLot.vacateVehicle(cmd[1]);
                    break;
                case "display":
                    parkingLot.displayBoard(cmd[1], cmd[2]);
                    break;
                case "exit":
                    break outerloop;
            }
        }
    }
}

ParkingStrategy.java

package ParkingLot.ParkingStrategy;

import ParkingLot.ParkingLot;
import ParkingLot.ParkingSpot;
import ParkingLot.Vehicle;

public interface ParkingStrategy {

    ParkingSpot getAvailableSpot(ParkingLot parkingLot, Vehicle vehicle);
}

NearestAvailableSlotStrategy.java

package ParkingLot.ParkingStrategy;

import ParkingLot.ParkingLot;
import ParkingLot.ParkingSpot;
import ParkingLot.ParkingFloor;
import ParkingLot.Vehicle;

import java.util.Map;

public class NearestAvailableSlotStrategy implements ParkingStrategy {

    @Override
    public ParkingSpot getAvailableSpot(ParkingLot parkingLot, Vehicle vehicle) {
        for(Map.Entry<Integer, ParkingFloor> entry: parkingLot.getFloorMap().entrySet()){
            ParkingFloor floor = entry.getValue();
            if(floor.canPark(vehicle)){
                return floor.getParkingSpot(vehicle);
            }
        }
        return null;
    }
}

ParkingLot.java

package ParkingLot;

import ParkingLot.ParkingStrategy.NearestAvailableSlotStrategy;
import ParkingLot.ParkingStrategy.ParkingStrategy;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ParkingLot {

    private final String parkingLotId;
    private final Integer noOfFloors;
    private final Integer noOfSpotsPerFloor;

    private Map<Integer, ParkingFloor> floorMap;
    private Map<String, ParkingTicket> ticketMap;

    private final ParkingStrategy parkingStrategy;

    public static final Integer RESERVED_SLOT_FOR_TRUCK = 1;
    public static final List<Integer> RESERVED_SLOT_FOR_BIKE = List.of(2, 3);

    public ParkingLot(String parkingLotId, String noOfFloors, String noOfSpotsPerFloor){
        this.parkingLotId = parkingLotId;
        this.noOfFloors = Integer.parseInt(noOfFloors);
        this.noOfSpotsPerFloor = Integer.parseInt(noOfSpotsPerFloor);
        floorMap = new HashMap<>();
        ticketMap = new HashMap<>();
        parkingStrategy = new NearestAvailableSlotStrategy();
    }

    public String getParkingLotId() {
        return parkingLotId;
    }

    public Map<String, ParkingTicket> getTicketMap() {
        return ticketMap;
    }

    public Map<Integer, ParkingFloor> getFloorMap() {
        return floorMap;
    }

    public void initializeParkingLot(){
        for(int floorNum = 1; floorNum <= noOfFloors; floorNum++){
            ParkingFloor floor = new ParkingFloor(floorNum);
            for(int spotNum = 1; spotNum <= noOfSpotsPerFloor; spotNum++){
                ParkingSpot parkingSpot = new ParkingSpot(floorNum, spotNum);
                if(spotNum == RESERVED_SLOT_FOR_TRUCK) {
                    parkingSpot.setType(ParkingSpotType.TRUCK);
                }
                else if(RESERVED_SLOT_FOR_BIKE.contains(spotNum)) {
                    parkingSpot.setType(ParkingSpotType.BIKE);
                }
                else {
                    parkingSpot.setType(ParkingSpotType.CAR);
                }
                floor.addParkingSpot(parkingSpot);
            }
            floorMap.put(floorNum, floor);
        }
        System.out.println("Created parking lot with "+ noOfFloors +" floors and "+ noOfSpotsPerFloor + " slots per floor");
    }

    public void addFloor(ParkingFloor floor){
        floorMap.put(floor.getFloorId(), floor);
    }

    public void addSpot(String floorId, ParkingSpot parkingSpot){
        ParkingFloor floor = floorMap.get(floorId);
        floor.addParkingSpot(parkingSpot);
    }

    public ParkingTicket generateParkingTicket(String parkingLotId, Integer floorId, Integer spotId){
        String id = parkingLotId + "_" + floorId + "_" + spotId;
        ParkingTicket ticket = new ParkingTicket(id, floorId, spotId);
        return ticket;
    }

    public void parkVehicle(Vehicle vehicle){
        ParkingSpot parkingSpot = parkingStrategy.getAvailableSpot(this, vehicle);
        if(parkingSpot == null){
            System.out.println("Parking Lot full");
            return;
        }
        ParkingTicket ticket = this.generateParkingTicket(this.getParkingLotId(), parkingSpot.getFloorId(), parkingSpot.getSpotId());
        parkingSpot.assignVehicle(vehicle);
        vehicle.assignTicket(ticket);
        ParkingFloor floor = floorMap.get(parkingSpot.getFloorId());
        floor.moveSpotFromAvailableMapToOccupiedMap(parkingSpot);
        ticketMap.put(ticket.getTicketId(), ticket);
        return;
    }

    public void vacateVehicle(String ticketId){
        if(!ticketMap.containsKey(ticketId)) {
            System.out.println("Invalid Ticket");
            return;
        }
        ParkingTicket parkingTicket = ticketMap.get(ticketId);
        ParkingFloor parkingFloor = floorMap.get(parkingTicket.getFloorId());
        ParkingSpot parkingSpot = parkingFloor.getParkingSpot(parkingTicket.getSpotId());
        Vehicle vehicle = parkingSpot.getVehicle();
        parkingSpot.vacateVehicle();
        ParkingFloor floor = floorMap.get(parkingSpot.getFloorId());
        floor.moveSpotFromOccupiedMapToAvailableMap(parkingSpot);
        System.out.println("Unparked vehicle with Registration Number: "+
                vehicle.getRegistrationNumber() +" and Color: "+ vehicle.getColor());
        //this.scanAndPay()
        ticketMap.remove(ticketId);
    }

    public void displayBoard(String displayType, String vehicleType){
        for(ParkingFloor parkingFloor: floorMap.values()){
            switch (vehicleType){
                case "CAR":
                    parkingFloor.displayFloorBoard(displayType, VehicleType.CAR);
                    break;
                case "BIKE":
                    parkingFloor.displayFloorBoard(displayType, VehicleType.BIKE);
                    break;
                case "TRUCK":
                    parkingFloor.displayFloorBoard(displayType, VehicleType.TRUCK);
                    break;
            }
        }
    }
}

ParkingFloor.java

package ParkingLot;

import java.util.HashMap;
import java.util.Map;

public class ParkingFloor {

    private Integer floorId;
    private Map<ParkingSpotType, Map<Integer, ParkingSpot>> availableSpots;
    private Map<ParkingSpotType, Map<Integer, ParkingSpot>> occupiedSpots;
    private Map<Integer, ParkingSpot> spotMap;

    public ParkingFloor(Integer floorId){
        this.floorId = floorId;
        spotMap = new HashMap<>();
        availableSpots = new HashMap<>();
        availableSpots.put(ParkingSpotType.BIKE, new HashMap<>());
        availableSpots.put(ParkingSpotType.CAR, new HashMap<>());
        availableSpots.put(ParkingSpotType.TRUCK, new HashMap<>());

        occupiedSpots = new HashMap<>();
        occupiedSpots.put(ParkingSpotType.BIKE, new HashMap<>());
        occupiedSpots.put(ParkingSpotType.CAR, new HashMap<>());
        occupiedSpots.put(ParkingSpotType.TRUCK, new HashMap<>());
    }

    public Integer getFloorId() {
        return floorId;
    }

    public void addParkingSpot(ParkingSpot parkingSpot){
        Map<Integer, ParkingSpot> map = availableSpots.get(parkingSpot.getType());
        map.put(parkingSpot.getSpotId(), parkingSpot);
        availableSpots.put(parkingSpot.getType(), map);
        spotMap.put(parkingSpot.getSpotId(), parkingSpot);
    }

    public ParkingSpot getParkingSpot(Integer parkingSpotId){
        return spotMap.get(parkingSpotId);
    }

    public ParkingSpotType getParkingSpotTypeForVehicle(VehicleType vehicleType){
        switch (vehicleType){
            case BIKE:
                return ParkingSpotType.BIKE;
            case TRUCK:
                return ParkingSpotType.TRUCK;
            default:
                return ParkingSpotType.CAR;
        }
    }

    public boolean canPark(Vehicle vehicle){
        ParkingSpotType parkingSpotType = getParkingSpotTypeForVehicle(vehicle.getType());
        return availableSpots.get(parkingSpotType).size() > 0;
    }

    public ParkingSpot getParkingSpot(Vehicle vehicle){
        ParkingSpotType parkingSpotType = getParkingSpotTypeForVehicle(vehicle.getType());
        for(Map.Entry<Integer, ParkingSpot> entry: availableSpots.get(parkingSpotType).entrySet()){
            return entry.getValue();
        }
        return null;
    }

    public void moveSpotFromAvailableMapToOccupiedMap(ParkingSpot parkingSpot){
        availableSpots.get(parkingSpot.getType()).remove(parkingSpot.getSpotId());
        occupiedSpots.get(parkingSpot.getType()).put(parkingSpot.getSpotId(), parkingSpot);
    }

    public void moveSpotFromOccupiedMapToAvailableMap(ParkingSpot parkingSpot){
        occupiedSpots.get(parkingSpot.getType()).remove(parkingSpot.getSpotId());
        availableSpots.get(parkingSpot.getType()).put(parkingSpot.getSpotId(), parkingSpot);
    }

    public void displayFloorBoard(String displayType, VehicleType vehicleType){
        switch (displayType){
            case "free_count":
                int count = availableSpots.get(getParkingSpotTypeForVehicle(vehicleType)).size();
                System.out.println("No. of free slots for " + vehicleType.toString() + " on Floor "+ floorId +": "+ count);
                break;
            case "free_slots":
                System.out.print("Free slots for "+ vehicleType.toString() + " on Floor "+ floorId +": ");
                for(Map.Entry<Integer, ParkingSpot> entry:
                        availableSpots.get(getParkingSpotTypeForVehicle(vehicleType)).entrySet()){
                    System.out.print(entry.getKey()+",");
                }
                System.out.println();
                break;
            case "occupied_slots":
                System.out.print("Occupied slots for "+ vehicleType.toString() + " on Floor "+ floorId +": ");
                for(Map.Entry<Integer, ParkingSpot> entry:
                        occupiedSpots.get(getParkingSpotTypeForVehicle(vehicleType)).entrySet()){
                    System.out.print(entry.getKey()+",");
                }
                System.out.println();
                break;
        }
    }
}

ParkingSpot.java

package ParkingLot;

public class ParkingSpot {

    private Integer spotId;
    private Integer floorId;
    private ParkingSpotType type;
    private Vehicle vehicle;
    private Boolean isSpotFree;

    ParkingSpot(Integer floorId, Integer spotId){
        this.floorId = floorId;
        this.spotId = spotId;
        isSpotFree = true;
    }

    public Vehicle getVehicle() {
        return vehicle;
    }

    public void setType(ParkingSpotType type) {
        this.type = type;
    }

    public Integer getFloorId() {
        return floorId;
    }

    public ParkingSpotType getType() {
        return type;
    }

    public Integer getSpotId() {
        return spotId;
    }

    public Boolean getIsSpotFree() {
        return isSpotFree;
    }

    public void assignVehicle(Vehicle vehicle){
        this.vehicle = vehicle;
        isSpotFree = false;
    }

    public void vacateVehicle(){
        this.vehicle = null;
        isSpotFree = true;
    }
}

ParkingSpotType.java

package ParkingLot;

public enum ParkingSpotType {
    CAR, BIKE, TRUCK
}

Vehicle.java

package ParkingLot;

public class Vehicle {


    private String registrationNumber;
    private VehicleType type;
    private String color;
    private ParkingTicket ticket;

    public Vehicle(String type, String registrationNumber, String color){
        switch (type){
            case "CAR":
                this.type = VehicleType.CAR;
                break;
            case "BIKE":
                this.type = VehicleType.BIKE;
                break;
            case "TRUCK":
                this.type = VehicleType.TRUCK;
                break;
        }
        this.color = color;
        this.registrationNumber = registrationNumber;

    }

    public String getColor() {
        return color;
    }

    public void assignTicket(ParkingTicket ticket){
        this.ticket = ticket;
        System.out.println("Parked vehicle. Ticket ID: " + ticket.getTicketId());
    }

    public String getRegistrationNumber() {
        return registrationNumber;
    }

    public VehicleType getType() {
        return type;
    }
}

VehicleType.java

package ParkingLot;

public enum VehicleType {
    CAR, BIKE, TRUCK
}

ParkingTicket.java

package ParkingLot;

public class ParkingTicket {

    private String ticketId;
    private Integer floorId;
    private Integer spotId;

    public ParkingTicket(String ticketId, Integer floorId, Integer spotId){
        this.ticketId = ticketId;
        this.floorId = floorId;
        this.spotId = spotId;
    }

    public String getTicketId() {
        return ticketId;
    }

    public Integer getFloorId() {
        return floorId;
    }

    public Integer getSpotId() {
        return spotId;
    }
}
Comments (7)