I have designed below system for the parking lot:
Note: please ignore logic and exit class ( this should be abstract but for now I have not kept it like that)
I would really appreciate you feedback and suggestions. It would be really helpful if I get to know the weak points as well as strong ones:

export abstract class Entrance {
private managerFactory: ParkingSlotManagerfactory;
constructor(managerfactory: ParkingSlotManagerfactory) {
this.managerFactory = managerfactory;
}
findParkingandGenerateTicket(
vehicle: Vehicle,
charge: PARKING_PRICE_STRATEGY = PARKING_PRICE_STRATEGY.HOURLY
) {
const manager = this.managerFactory.getManager(
vehicle.getVehicleType()
);
const parkingSlot = manager.findParkingSlot();
parkingSlot?.parkVehicle(vehicle);
return new Ticket(vehicle, parkingSlot);
}
}
export class MainEntrance extends Entrance {
constructor(managerFactory: ParkingSlotManagerfactory) {
super(managerFactory);
}
}
export class Exit {
private managerFactory: ParkingSlotManagerfactory;
constructor(managerFactory: ParkingSlotManagerfactory) {
this.managerFactory = managerFactory;
}
calculatePrice(ticket: Ticket) {
const manager = this.managerFactory.getManager(
ticket.vehicle.getVehicleType()
);
const price = CalculatePrice.calculatePrice(ticket);
}
}
export class HourlyPriceStrategy implements PriceStrategy {
calculatePrice(ticket: Ticket): number {
const hoursParked =
(new Date().getTime() - ticket.entryTime.getTime()) /
3600000;
console.log(hoursParked);
console.log(
Math.ceil(hoursParked) * ticket.parkingSlot.price
);
return 15;
}
}
export class MinutePriceStrategy implements PriceStrategy {
calculatePrice(ticket: Ticket): number {
return 10;
}
}
export interface PriceStrategy {
calculatePrice(ticket: Ticket): number;
}
export abstract class ParkingSlot {
id: number;
isOccupied: boolean;
isOccupiedBy: Vehicle | null;
slotType: ParkingSlotType;
price: PARKINGPRICE;
constructor(
id: number,
slotType: ParkingSlotType,
price: PARKINGPRICE
) {
this.id = id;
this.isOccupied = false;
this.slotType = slotType;
this.isOccupiedBy = null;
this.price = price;
}
parkVehicle(vehicle: Vehicle) {
this.isOccupiedBy = vehicle;
this.isOccupied = true;
}
}
export class CarSlot extends ParkingSlot {
constructor(id: number) {
super(id, ParkingSlotType.CAR, PARKINGPRICE.CAR);
}
}
export class BikeSlot extends ParkingSlot {
constructor(id: number) {
super(id, ParkingSlotType.BIKE, PARKINGPRICE.BIKE);
}
}
export abstract class ParkingSlotManager {
slotList: Array<ParkingSlot>;
constructor(slotLists: Array<ParkingSlot>) {
this.slotList = slotLists;
}
findParkingSlot(): ParkingSlot {
if (this.slotList.length === 0) {
throw new Error("No available parking slot");
}
return this.slotList.pop()!;
}
addParkingSlot(parkingSlot: ParkingSlot) {
this.slotList.push(parkingSlot);
}
removeParkingSlot() {}
parkVehicle() {}
unparkVehicle() {}
}
export class CarParkingManager extends ParkingSlotManager {
constructor(slotList: ParkingSlot[]) {
super(slotList);
}
}
export class BikeParkingManager extends ParkingSlotManager {
constructor(slotList: Array<ParkingSlot>) {
super(slotList);
}
}
export class CalculatePrice {
static calculatePrice(ticket: Ticket) {
const strategy =
PakringPriceStrategyFactory.getPriceStrategy(
ticket.chargeBy
);
strategy.calculatePrice(ticket);
}
}
export abstract class Vehicle {
private vehicleNumber: string;
private vehicleType: VehicleType;
constructor(
vehicleNumber: string,
vehicleType: VehicleType
) {
this.vehicleType = vehicleType;
this.vehicleNumber = vehicleNumber;
}
getVehicleNumber(): string {
return this.vehicleNumber;
}
getVehicleType(): VehicleType {
return this.vehicleType;
}
setVehicleType(vehicle: VehicleType) {
this.vehicleType = vehicle;
}
setVehicleNumber(vehicleNumber: string) {
this.vehicleNumber = vehicleNumber;
}
}
export class Car extends Vehicle {
constructor(vehicleNumber: string) {
super(vehicleNumber, VehicleType.Car);
}
}
export class Bike extends Vehicle {
constructor(vehicleNummber: string) {
super(vehicleNummber, VehicleType.Bike);
}
}
export class Ticket {
id: number;
vehicle: Vehicle;
entryTime: Date;
exitTime: Date | undefined;
status: TicketStatus;
parkingSlot: ParkingSlot;
chargeBy: PARKING_PRICE_STRATEGY;
constructor(
vehicle: Vehicle,
parking: ParkingSlot,
chargeBy: PARKING_PRICE_STRATEGY = PARKING_PRICE_STRATEGY.HOURLY
) {
this.id = Math.floor(Math.random() * 1000000);
this.vehicle = vehicle;
this.entryTime = new Date();
this.status = TicketStatus.PENDING;
this.parkingSlot = parking;
this.chargeBy = chargeBy;
}
}
export class ParkingSlotManagerfactory {
private carManager: CarParkingManager;
private bikeManager: BikeParkingManager;
constructor(
carManager: CarParkingManager,
bikeManager: BikeParkingManager
) {
this.carManager = carManager;
this.bikeManager = bikeManager;
}
getManager(vehicleType: VehicleType): ParkingSlotManager {
switch (vehicleType) {
case VehicleType.Car:
return this.carManager;
case VehicleType.Bike:
return this.bikeManager;
default:
throw new Error("Invalid vehicle type");
}
}
}
export class PakringPriceStrategyFactory {
static getPriceStrategy(
priceStrategy: PARKING_PRICE_STRATEGY
) {
switch (priceStrategy) {
case PARKING_PRICE_STRATEGY.HOURLY:
return new HourlyPriceStrategy();
case PARKING_PRICE_STRATEGY.MINUTE:
return new MinutePriceStrategy();
default:
throw new Error(
`Invalid price strategy: ${priceStrategy}`
);
}
}
}
export enum VehicleType {
Car = "car",
Bike = "bike",
}
export enum TicketStatus {
PENDING = "pending",
PAID = "paid",
CANCELLED = "cancelled",
}
export enum PARKING_PRICE_STRATEGY {
HOURLY = "Hour",
MINUTE = "minute",
}
export enum ParkingSlotType {
CAR = "Car",
BIKE = "Bike",
}
export enum PARKINGPRICE {
CAR = 50,
BIKE = 30,
}
// main class
let carSlotList = [];
let bikeSlotList = [];
for (let i = 0; i < 10; i++) {
carSlotList.push(new CarSlot(i + 1));
}
for (let i = 0; i < 10; i++) {
bikeSlotList.push(new BikeSlot(i + 1));
}
const carParkingManager = new CarParkingManager(
carSlotList
);
const bikeParkingManager = new BikeParkingManager(
bikeSlotList
);
const managerFactory = new ParkingSlotManagerfactory(
carParkingManager,
bikeParkingManager
);
const mainEntrance = new MainEntrance(managerFactory);
const exit = new Exit(managerFactory);
const car = new Car("DL6s437676");
console.log(car.getVehicleType());
const ticket =
mainEntrance.findParkingandGenerateTicket(car);
console.log(ticket);
exit.calculatePrice(ticket);