Determine the different ways the system will be used. This includes main functions the system needs to perform and who will use it.
Parking Spot Management:
Vehicle Management:
Parking Assignment:
Fee Calculation:
User Interface:
Based on the requirements and use cases, identify the main objects of the system...
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.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...
The Singleton Design Pattern has been implemented for the Parking Lot class, ensuring that only one instance of the Parking Lot is shared throughout the application. This is crucial for managing the parking spots consistently and avoiding conflicts that occur when multiple instances attempt to access shared resources.
class ParkingLot{
private static ParkingLot instance=null;
private ParkingLot(){}
ParkingLot getInstance()
{
if(instance==null)return new ParkingLot();
else return instance;
}
//other methods like
//adding spots
// assignSpots
}
The VehicleFactory utilizes the Factory Design Pattern for effective object creation of various vehicle types. This pattern allows for the instantiation of different vehicle objects based on specific conditions, such as the vehicle category (e.g., car, motorcycle, truck) or the required attributes (e.g., capacity, size). This approach promotes loose coupling and enhances the maintainability of the code.
public class VehicleFactory {
public static Vehicle createVehicle(String type, String licensePlate) {
switch(type.toLowerCase()) {
case "car":
return new Car(licensePlate);
case "bike":
return new Bike(licensePlate);
case "truck":
return new Truck(licensePlate);
default:
throw new IllegalArgumentException("Unknown vehicle type");
}
}
}
The Strategy Design Pattern is implemented for defining the pricing strategy for different vehicle types. For instance, in our example, cars have a discounted fee strategy, while other vehicles work with a default pricing strategy. This separation allows for flexible pricing adjustments based on vehicle type and ensures the underlying logic can be modified independently of the vehicle classes.
interface FeeStategy{
static int calculateFee1(Date startDate, Date endDate) {
return 0;
}
}
class DiscountFee implements FeeStategy {
public static int calculateFee1(Date startDate, Date endDate) {
// return endDate-startDate;
return 9;
}
}
class DefaultStrategy implements FeeStategy{
public static int calculateFee1(Date startDate, Date endDate) {
// String i = endDate - startDate;
// return i.length();
return 0;
}
}
public class Ticket{
Feestrategy feestrategy;
int calculateFee()
{
if(vehicle.getType()== VehicleType.CAR)
return DiscountFee.calculateFee1(EntryTime,new Date());
else
return DefaultStrategy.calculateFee1(EntryTime,new Date());
}
}
The Observer Design Pattern can be employed here to notify subscribers (observers) when a parking spot becomes available or when the allocated parking time for a vehicle is nearing completion. By using this design pattern, we ensure that all interested parties receive timely updates and can react accordingly, which enhances the dynamic interaction within our parking system.
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.
ParkingLot->
public class ParkingLot {
private static ParkingLot instance = null;
List<ParkingSpot>li=new ArrayList<>();
private ParkingLot()
{
}
void addSpot(){
li.add(new ParkingSpot(null,null,true));
li.add(new ParkingSpot(null,null,true));
li.add(new ParkingSpot(null,null,true));
li.add(new ParkingSpot(null,null,true));
}
public static synchronized ParkingLot getInstance() {
if(instance == null) {
instance=new ParkingLot();
}
return instance;
}
ParkingSpot AssignSpot(Vehicle vehicle)
{
for(ParkingSpot sp:li)
{
if(sp.isAvailable==true)
{
return sp.assignVehcile(vehicle);
}
}
return null;
}
}
ParkingSpot->
public class ParkingSpot
{
Vehicle vehicle;
VehicleType vehicleType;
boolean isAvailable;
Ticket ticket;
public ParkingSpot(VehicleType vehicleType, Vehicle vehicle, boolean isAvailable) {
this.vehicleType = vehicleType;
this.vehicle = vehicle;
this.isAvailable = isAvailable;
}
boolean check()
{
return isAvailable==true;
}
ParkingSpot assignVehcile(Vehicle vehicle)
{
this.vehicle = vehicle;
this.vehicleType=vehicle.getType();
isAvailable=false;
ticket=new Ticket(new Date(),1,this,this.vehicle);
return this;
}
void removeVehicle(Vehicle vehicle)
{
this.vehicle=null;
this.vehicleType=null;
isAvailable=true;
System.out.println(this.ticket.calculateFee());
}
}
Vehicle->
public class Vehicle
{
String licenceNumber;
VehicleType vehicleType;
Vehicle(String licenceNumber, VehicleType vehicleType)
{
this.licenceNumber = licenceNumber;
this.vehicleType = vehicleType;
}
public VehicleType getType()
{
return vehicleType;
}
}
Ticket->
interface FeeStategy{
static int calculateFee1(Date startDate, Date endDate) {
return 0;
}
}
class DiscountFee implements FeeStategy {
public static int calculateFee1(Date startDate, Date endDate) {
return 9;
}
}
class DefaultStrategy implements FeeStategy{
public static int calculateFee1(Date startDate, Date endDate) {
return 0;
}
}
public class Ticket {
int ticketID;
Date EntryTime;
Date ExitTime;
Vehicle vehicle;
ParkingSpot parkingSpot;
FeeStategy feeStrategy;
public Ticket( Date entryTime, int ticketID, ParkingSpot parkingSpot, Vehicle vehicle) {
EntryTime = entryTime;
this.ticketID = ticketID;
this.parkingSpot = parkingSpot;
this.vehicle = vehicle;
}
int calculateFee()
{
if(vehicle.getType()== VehicleType.CAR)
return DiscountFee.calculateFee1(EntryTime,new Date());
else
return DefaultStrategy.calculateFee1(EntryTime,new Date());
}
}
Check and explain whether your design adheres to solid principles (Ask interviewer what SOLID principle is if you can not recall it.)...
Each class in our design has a single responsibility. For example, ParkingLot manages the overall parking lot, ParkingSpot manages an individual spot, and Ticket handles parking tickets. This clear separation ensures that changes in one aspect of the system don’t affect other parts.
The use of inheritance allows for extending functionality without modifying existing classes. For instance, new vehicle types (e.g., ElectricCar) can be added by creating new subclasses of Vehicle without altering the existing Vehicle class. Similarly, new fee calculation strategies can be added by implementing new FeeStrategy classes.
Subclasses like Car, Bike, and Truck can be used interchangeably with the Vehicle superclass. The system’s behavior remains consistent regardless of the specific vehicle type, adhering to LSP.
Although we haven't explicitly defined interfaces, our design ensures that classes only contain methods relevant to their specific role. For example, Vehicle only includes methods related to vehicle characteristics, and ParkingSpot focuses on spot management.
The use of strategies for fee calculation (e.g., FeeStrategy interface) allows high-level classes like Ticket to depend on abstractions rather than concrete implementations. This makes the system more flexible and easier to modify or extend.
Explain how your design can handle changes in scale and whether it would be easily to extend with new functionalities...
Horizontal Scaling (Adding More Floors and Spots): New floors can be added by simply instantiating additional ParkingFloor objects and adding them to the ParkingLot. Similarly, new spots can be added to any floor by adding ParkingSpot objects to the corresponding ParkingFloor.
Vertical Scalling -> Database can be shard and partioned for the floor wise. It will make our database distributed and can handle large amount of request to out database .
Flexibility
The system's design supports the easy integration of new vehicle types through the use of the Factory Design Pattern. By creating subclasses of a shared Vehicle interface, such as ElectricCar, developers can introduce new vehicle types without altering existing code. This modular architecture, coupled with configuration files for dynamic attribute loading, ensures the system remains maintainable and adaptable.
The Strategy Design Pattern facilitates the addition of new payment methods by allowing them to be implemented as interchangeable strategies. For instance, adding a DigitalWallet payment option involves simply creating a new class that adheres to the PaymentStrategy interface. The use of configuration files also enables dynamic updates, allowing administrators to integrate new payment methods without needing to modify the codebase.
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.
Automatic Spot Assignment ->Our System should assign automatic spot to the user efficiently means depending upon the vehicle entry we can assign the spot nearest to the car .
Notification System -> In case of busy Days user can subscribe to the parking lot and gets notification if any spot gets available . We can achieve this with the help of observer design pattern.