parking spot assignment based on vehicle size and spot availability
spot availability checking:
fee calculation based on parking duration
Support multiple floors
user roles and permissions:
ParkingSpot
ParkingFloor
ParkingLot
Vehicle
Ticket
user
Determine how these objects will interact with each other to fulfill the use cases...
ParkingLot contains multiple ParkingFloor objects. It manages the overall parking facility and delegates the task of finding an available spot to the appropriate floor.ParkingFloor contains multiple ParkingSpot objects. The floor finds and assigns an available spot to a vehicle based on its size.ParkingSpot is either occupied by a Vehicle or is vacant. When occupied, the spot stores a reference to the vehicle.Ticket is generated, recording the entry time, the assigned spot, and the vehicle. The ticket is used to calculate the parking fee.Payment is made for a Ticket. The payment records the amount paid and marks the ticket as settled.Users interact with the system based on their roles (Admin, Attendant, Regular User), performing actions like spot reservation, vehicle check-in/out, and viewing parking history.Design inheritance trees where applicable to promote code reuse and polymorphism. This step involves identifying common attributes and behaviors that can be abstracted into parent classes...
ParkingSpot that will contain common attributes like spotId, size, and isOccupied.CompactSpot, LargeSpot, and HandicappedSpot, each inheriting from ParkingSpot.Vehicle will represent general attributes like licensePlate and size.Car, Bike, and Truck can inherit from Vehicle to handle vehicle-specific details.User class will serve as a base class with common attributes like userId, name, and role.Admin, Attendant, and RegularUser can inherit from User to implement role-specific behaviors.Consider using design patterns (e.g., Factory, Singleton, Observer, Strategy) that fit the problem...
Factory Design pattern for Vehicle - build which type of vehicle object based on the vehicle type
public class VehicleFactory(String type, String licenseNumber){
switch(type.toLowerCase()){
case "car":
return new Car(licenseNumber, "medium");
case "bike":
return new Bike(licenseNumber, "small");
case "truck":
return new Truck(licenseNumber, "large");
default:
throw new IllegalArgumentException("unknown type");
}
}
The ParkingLot can be a singleton, ensuring that there is only one instance managing the entire parking facility.
public class ParkingLot {
private static ParkingLot instance;
private ParkingLot(){
}
public static synchronized ParkingLot getInstance(){
if (instance == null){
instance = new ParkingLot();
}
return instance;
}
}
Strategy Pattern
This can be applied for calculating parking fees. Different strategies can be implemented based on vehicle type, parking duration, or special discounts.
public interface FeeStrategy {
double calculateFee(Ticket ticket);
}
// Polymorphism
public class RegularStrategy implements FeeStrategy {
public double calculateFee (Ticket ticket) {
// basic fee calculation
}
}
public class PremiumStrategy implements FeeStrategy {
public double calculateFee (Ticket ticket) {
// premium fee calculation
}
}
public class Ticket {
private FeeStrategy feeStrategy;
public void setFeeStrategy(FeeStrategy feeStrategy) {
this. feeStrategy = feeStrategy;
}
public double calculateFee(){
return feeStrategy.calculateFee(this);
}
}
Observer Pattern
This can be used to notify users (Admin, Attendant) when certain events occur, such as a parking spot becoming available or a payment being processed.
public interface ParkingObserver {
void update (String eventType, ParkingSpot parkingSpot);
}
public class User implements ParkingObserver {
public void update (String eventType, ParkingSpot parkingSpot){
if (role== "admin") {
// Handle event based on eventType for Admin
} else {
// Handle event based on eventType for customers
}
}
}
Attributes: For each class, define the attributes (data) it will hold...
Methods: Define the methods (functions) that operate on the attributes. Ensure they align with the object's responsibilities and adhere to the principle of encapsulation.
public class VehicleFactory {
Public static Vehicle createVehicle(String type, String licenseNumber) {
switch(type.toLowerCase()){
case "car":
return new Car(licenseNumber, "medium");
case "bike":
return new Bike(licenseNumber, "small");
case "truck":
return new Truck(licenseNumber, "large");
default:
throw new IllegalArgumentException("unknown type");
}
}
}
public abstract class Vehicle {
protected String licenseNumber;
protected String size;
public Vehicle(String licenseNumber, String size){
this.licenseNumber = licenseNumber;
this.size = size;
}
public String getLicenseNumber (){
return licensePlate;
}
public String getSize() {
return size;
}
}
public class Car extends Vehicle {
public Car (String licenseNumber){
super(licenseNumber, "medium");
}
}
public class Bike extends Vehicle {
public Bike (String licenseNumber){
super(licenseNumber, "small");
}
}
public class Truck extends Vehicle {
public Truck (String licenseNumber){
super(licenseNumber, "large");
}
}
public class ParkingSpot {
private String spotId;
private String size;
private Vehicle vehicle;
private boolean isOccupied;
public ParkingSpot (spotId, String size) {
this.spotId = spotId;
this.size = size;
this.isOccupied = false;
this.vehicle = null;
}
public void parkVehicle (Vehicle vehicle) {
this.vehicle = vehicle;
this.isOccupied = true;
}
public void removeVehicle() {
this.vehicle = null;
this.isOccupied = false;
}
public boolean isOccupied() {
return this.isOccupied;
}
public String getSize() {
return size;
}
}
public Class ParkingFloor {
private String floorId;
private List <ParkingSpot> parkingSpots;
private int totalSpots;
private int availableSpots;
public ParkingFloor (String floorId, List<ParkingSpot> spots) {
this.floorId = floorId;
this.parkingSpots= spots;
this.totalSpots = parkingSpots.size();
this.availableSpots = calculateAvailableSpots();
}
private int calculateAvailableSpots() {
int count = 0;
for (ParkingSpot parkingSpot : parkingSpots) {
if (!parkingSpot.isOccupied()) count ++;
}
return count;
}
public ParkingSpot findAvailableSpot(Vehicle v) {
for (ParkingSpot ps : parkingSpots) {
if (!ps.isOccupied() && spot.getSize() == v.getSize()){
spot.parkVehicle(v);
availableSpots --;
return ps;
}
}
return null; // No spot available
}
}
public class ParkingLot {
private static ParkingLot instance;
private List<ParkingFloor> parkingFloors;
private int totalSpots;
private int availableSpots;
private ParkingLot(){
this.parkingFloors= new ArrayList<>();
}
// Singleton Design Pattern
public static synchronized ParkingLot getInstance (){
if (instance == null) {
instance = new ParkingLot();
}
return instance;
}
public void addFloor(ParkingFloor floor) {
parkingFloors.add(floor);
totalSpots += floor.getTotalSpots();
avialableSpots += floor.getAvailableSpots();
}
public ParkingSpot findAvailableSpot(Vehicle vehicle) {
for (ParkingFloor floor : floors) {
ParkingSpot spot = floor.findAvailableSpot(vehicle);
if (spot != null) return spot;
}
return null; // No spot available
}
public void releaseSpot(ParkingSpot spot) {
spot.removeVehicle();
availableSpots++;
}
}
public class Ticket {
private String ticketId;
private Vehicle vehicle;
private ParkingSpot spot;
private Date entryTime;
private Date exitTime;
private double fee;
public Ticket(String ticketId, Vehicle vehicle, ParkingSpot spot) {
this.ticketId = ticketId;
this.vehicle = vehicle;
this.spot = spot;
this.entryTime = new Date(); // Set entry time to current time
}
public void markExit() {
this.exitTime = new Date(); // Set exit time to current time
calculateFee();
}
private void calculateFee() {
// Fee calculation logic, possibly using a strategy pattern
long duration = exitTime.getTime() - entryTime.getTime(); // Duration in milliseconds
this.fee = duration * 0.05; // Example fee calculation
}
public double getFee() {
return fee;
}
// Other ticket-related methods
}
Check and explain whether your design adheres to solid principles (Ask interviewer what SOLID principle is if you can not recall it.)...
Explain how your design can handle changes in scale and whether it would be easily to extend with new functionalities...
Try creating a class, flow, state and/or sequence diagram using the diagramming tool. Mermaid flow diagrams can be used to represent system use cases. You can ask the interviewer bot to create a starter diagram if unfamiliar with the tool. Briefly explain your diagrams if necessary...
Critically examine your design for any flaws or areas for future improvement...