Requirements
Determine the different ways the system will be used. This includes main functions the system needs to perform and who will use it.
parking lot will check available empty parking spots.
parking lot will assign vehicles to available parking spots.
parking lot will update available parking spots (increase and decrease).
vehicles can request to enter and leave parking spot.
Define Core Objects
Based on the requirements and use cases, identify the main objects of the system...
parkinglot, parking spot, vehicles
Analyze Relationships
Determine how these objects will interact with each other to fulfill the use cases...
parking lot has parking spots (composition relationship, one to many)
parking spot associate with vehicles (one to one)
Establish Hierarchy
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...
vehicles will be abstract base class, concrete implementations include cars, buses.
Design Patterns
Consider using design patterns (e.g., Factory, Singleton, Observer, Strategy) that fit the problem...
Composite
Define Class Members (write code)
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.
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Vehicle {
protected:
string m_name;
int m_spot{};
Vehicle(string name) : m_name{name}, m_spot{-1} {}
public:
virtual void print() const = 0;
int get_spot() const {
return m_spot;
}
void assign_spot(int spot) {
m_spot = spot;
}
void release_spot() {
m_spot = -1;
}
};
class Car : public Vehicle {
string m_model;
public:
Car(string name, string model) : Vehicle{name}, m_model{model} {}
void print() const override {
cout << m_name << m_model << " " << m_spot << endl;
}
};
class Bus : public Vehicle {
string m_color;
public:
Bus(string name, string color) : Vehicle{name}, m_color{color} {}
void print() const override {
cout << m_name << m_color << " " << m_spot << endl;
}
};
class ParkingLot;
class ParkingSpot {
int m_number{};
shared_ptr<Vehicle> m_vehicle;
public:
ParkingSpot(int number, shared_ptr<Vehicle> vehicle = nullptr)
: m_number{number}, m_vehicle{vehicle} {
}
void assign_vehicle(shared_ptr<Vehicle> vehicle) {
m_vehicle = vehicle;
}
friend class ParkingLot;
};
class ParkingLot {
int m_capacity{};
int m_available{};
vector<ParkingSpot> m_spots;
public:
ParkingLot(int capacity=100) : m_capacity{capacity}, m_available{m_capacity} {
for (int i = 0; i < m_capacity; i++) {
m_spots.emplace_back(i);
}
}
int get_available() const {
return m_available;
}
bool request_spot(shared_ptr<Vehicle> v) {
if (m_available == 0) {
return false;
}
for (ParkingSpot& spot : m_spots) {
if (spot.m_vehicle == nullptr) {
spot.assign_vehicle(v);
v->assign_spot(spot.m_number);
m_available--;
return true;
}
}
return false;
}
void release_spot(shared_ptr<Vehicle> v, int spot_number) {
m_spots[spot_number].m_vehicle = nullptr;
v->release_spot();
}
};
int main() {
// Write C++ code here
shared_ptr<Vehicle> car{make_shared<Car>("c1", "tesla")};
shared_ptr<Vehicle> bus{make_shared<Bus>("b1", "yellow")};
ParkingLot p{500};
cout << p.get_available() << endl;
car->print();
p.request_spot(car);
car->print();
bus->print();
p.request_spot(bus);
bus->print();
p.release_spot(car, car->get_spot());
car->print();
return 0;
}
Adhere to SOLID Guidelines
Check and explain whether your design adheres to solid principles (Ask interviewer what SOLID principle is if you can not recall it.)...
S: Each class is only responsible for one single thing, Vehicles manage themselves and the parking spot they are assigned to, parkingspots manage the number they are assigned and the vehicle on it, parkinglot manages the parkinglot and the parkingspots it has.
O: Vehicle class can be easily extended to more types (e.g. motorcycles), and the parkinglot system can be easily used for another parkinglot.
L: All derived vehicle classes can substitute the base class.
I: the derived vehicles program to an interface.
D: through use of private variables, the dependencies between the classes are minimal.
Consider Scalability and Flexibility
Explain how your design can handle changes in scale and whether it would be easily to extend with new functionalities...
new vehicle types can be easily added, parking lot capacity could be adjusted, and parking spot functionalities can be changed without the assigning and releasing logic being changed.
Create/Explain your diagram(s)
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...
Future improvements
Critically examine your design for any flaws or areas for future improvement...
Could maybe instead code parking spot inside parking lot to make it a composite instead of using friend classes.