Problem Statement
Design a parking lot that can handle vehicles entering and leaving while managing parking across multiple floors. Each vehicle should be assigned a suitable parking spot based on its type, and the spot should be freed once the vehicle exits. The design should also support generating a ticket at entry and optionally calculating the parking fee based on the duration of stay.
Functional Requirements
- The design should support multiple vehicle types such as bikes, cars, and trucks
- A vehicle must be assigned a parking spot compatible with its type
- A parking spot cannot be assigned to more than one vehicle at a time
- The parking lot should support multiple levels (floors)
- The design should search and allocate an available spot when a vehicle arrives
- A ticket should be generated when a vehicle is parked
- The ticket should store details such as entry time, vehicle, and parking spot
- When a vehicle leaves, the parking spot should be freed
- The design should support fee calculation based on parking duration
Objects Required
- Vehicle
- VehicleType
- ParkingSpot
- Level
- ParkingLot
- Ticket
- FeeCalculator
Vehicle Class
The Vehicle class is defined as an abstract class because different kinds of vehicles share common properties but may behave differently in the future.
public abstract class Vehicle {
String licenseNumber;
VehicleType type;
public Vehicle(String licenseNumber, VehicleType type) {
this.licenseNumber = licenseNumber;
this.type = type;
}
public VehicleType getType() {
return type;
}
}
The constructor ensures that every vehicle object is created with a license number and a type. This avoids partially initialized objects.
The getType() method is used by other classes when deciding where the vehicle can be parked. Instead of directly accessing the field, this method provides controlled access, which keeps the design flexible if the internal representation changes later.
VehicleType Enum
public enum VehicleType {
BIKE,
CAR,
TRUCK
}
The enum restricts the allowed vehicle categories to a fixed set. This removes the need for string comparisons and prevents invalid values from entering the design. It also makes the matching logic inside parking spots straightforward.
ParkingSpot Class
The ParkingSpot class models a single parking space and owns all logic related to whether a vehicle can be parked in it.
public class ParkingSpot {
int id;
VehicleType type;
boolean isOccupied;
Vehicle vehicle;
public ParkingSpot(int id, VehicleType type) {
this.id = id;
this.type = type;
this.isOccupied = false;
}
public boolean canFitVehicle(Vehicle vehicle) {
return this.type == vehicle.getType() && !isOccupied;
}
public void park(Vehicle vehicle) {
this.vehicle = vehicle;
this.isOccupied = true;
}
public void removeVehicle() {
this.vehicle = null;
this.isOccupied = false;
}
}
The constructor initializes the spot with its type and marks it as free.
The canFitVehicle() method checks compatibility by comparing the vehicle type and ensuring the spot is not already occupied. This logic is kept inside the class because the spot should decide whether it can accept a vehicle.
The park() method assigns the vehicle and updates the occupancy state together to maintain consistency.
The removeVehicle() method clears the vehicle reference and marks the spot as available again.
Level Class
The Level class groups multiple parking spots and represents a single floor.
import java.util.*;
public class Level {
int floorNumber;
List<ParkingSpot> spots;
public Level(int floorNumber, List<ParkingSpot> spots) {
this.floorNumber = floorNumber;
this.spots = spots;
}
public ParkingSpot findAvailableSpot(Vehicle vehicle) {
for (ParkingSpot spot : spots) {
if (spot.canFitVehicle(vehicle)) {
return spot;
}
}
return null;
}
}
The constructor associates a list of spots with a floor.
The findAvailableSpot() method iterates through the spots and returns the first one that can accommodate the vehicle. This keeps the search logic within the level instead of pushing it to a higher class.
Ticket Class
The Ticket class holds the details of a parked vehicle.
import java.time.LocalDateTime;
public class Ticket {
String ticketId;
Vehicle vehicle;
ParkingSpot spot;
LocalDateTime entryTime;
public Ticket(String ticketId, Vehicle vehicle, ParkingSpot spot) {
this.ticketId = ticketId;
this.vehicle = vehicle;
this.spot = spot;
this.entryTime = LocalDateTime.now();
}
}
The constructor captures the vehicle, the assigned spot, and the entry time at the moment the ticket is created. The entry time is required later for fee calculation.
FeeCalculator Class
The FeeCalculator class handles pricing logic separately.
import java.time.Duration;
import java.time.LocalDateTime;
public class FeeCalculator {
public static double calculateFee(Ticket ticket) {
Duration duration = Duration.between(ticket.entryTime, LocalDateTime.now());
long hours = duration.toHours();
return Math.max(10, hours * 20);
}
}
The calculateFee() method computes the duration between entry and exit and converts it into hours. The pricing logic is kept separate so that it can be modified without affecting parking-related classes.
ParkingLot Class
The ParkingLot class coordinates the overall flow.
import java.util.*;
public class ParkingLot {
List<Level> levels;
public ParkingLot(List<Level> levels) {
this.levels = levels;
}
public Ticket parkVehicle(Vehicle vehicle) {
for (Level level : levels) {
ParkingSpot spot = level.findAvailableSpot(vehicle);
if (spot != null) {
spot.park(vehicle);
return new Ticket(UUID.randomUUID().toString(), vehicle, spot);
}
}
return null;
}
public void unparkVehicle(Ticket ticket) {
ticket.spot.removeVehicle();
double fee = FeeCalculator.calculateFee(ticket);
System.out.println("Parking fee: " + fee);
}
}
The parkVehicle() method goes through each level and asks for an available spot. Once a spot is found, it parks the vehicle and creates a ticket by delegating responsibility to the respective classes.
The unparkVehicle() method removes the vehicle from its spot and calculates the fee using the ticket.
Comments
Post a Comment