This I have written as part of my LLD Practice. Please let me know further imporvements that can be done.
Also tell current design is Strong/Average/Lean Hire ?
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
Flow =>
Vehicle Enters => On entry alot a spot
On Exit => Based on pricing strategy, charge vehicle
*/
/* ======== Domain ======== */
enum VehicleType{
Car, Bike, Truck
}
enum SpotStatus{
Available, Occupied, UnderMaintainance
}
enum PaymentMode{
UPI,Cash,Card
}
abstract class Vehicle{
String vehicleId;
VehicleType vehicleType;
Vehicle(String vehicleId, VehicleType vehicleType){
this.vehicleId=vehicleId;
this.vehicleType=vehicleType;
}
}
class Car extends Vehicle{
Car(String id){
super(id,VehicleType.Car);
}
}
class Bike extends Vehicle{
Bike(String id){
super(id,VehicleType.Bike);
}
}
class Truck extends Vehicle{
Truck(String id){
super(id,VehicleType.Truck);
}
}
class VehicleFactory{
static Vehicle getVehicle(String id, VehicleType vehicleType){
switch (vehicleType){
case Car: return new Car(id);
case Bike: return new Bike(id);
case Truck: return new Truck(id);
default: throw new RuntimeException("Invalid vehicle Type");
}
}
}
class Spot{
String id;
VehicleType vehicleType;
SpotStatus spotStatus;
String floorId;
}
class Floor{
String id;
int floorNo;
List<Spot> spots;
Spot findAvailableSpot(Vehicle vehicle){
for(Spot spot:spots){
if(spot.vehicleType==vehicle.vehicleType && spot.spotStatus==SpotStatus.Available){
return spot;
}
}
return null;
}
}
class Ticket{
String id;
Vehicle vehicle;
Spot spot;
PaymentMode paymentMode;
LocalDateTime parkingTime;
LocalDateTime exitTime;
double amount;
boolean isActive;
Ticket(Vehicle vehicle, Spot spot, LocalDateTime parkingTime){
this.vehicle=vehicle;
this.spot=spot;
this.parkingTime=parkingTime;
}
}
interface PricingStrategy{
double calculatePrice(Vehicle vehicle, LocalDateTime entryTime, LocalDateTime exitTime);
}
class FlatPricingStrategy implements PricingStrategy{
@Override
public double calculatePrice(Vehicle vehicle, LocalDateTime entryTime, LocalDateTime exitTime){
return 100.0;
}
}
interface Payment{
void pay(double amount);
}
class UPIPayment implements Payment{
@Override
public void pay(double amount){
}
}
class CashPayment implements Payment{
@Override
public void pay(double amount){
}
}
class CardPayment implements Payment{
@Override
public void pay(double amount){
}
}
class PaymentFactory{
static public Payment getPaymentMethod(PaymentMode mode){
switch(mode){
case UPI: return new UPIPayment();
case Card: return new CardPayment();
case Cash: return new CashPayment();
default: throw new IllegalArgumentException("Invalid Method");
}
}
}
/* ======== Repository ======== */
class ParkingLotRepository{
//later can be used for admin support
Map<String,Floor> floorDb;
Map<String,Spot> spotDb;
void addFloor(Floor floor){
}
Floor getFloor(String floorId){
return floorDb.get(floorId);
}
List<Floor> getFloors(){
}
void addSpot(String floorId, Spot spot){
Floor floor=floorDb.get(floorId);
if(floor!=null){
floor.spots.add(spot);
spotDb.put(spot.id,spot);
}
}
Spot getSpot(String spotId){
return spotDb.get(spotId);
}
}
class TicketRepository{
Map<String,Ticket> ticketDb=new HashMap<>();
void addTicket(Ticket ticket){
ticketDb.put(ticket.id,ticket);
}
Ticket getTicket(String ticketId){
return ticketDb.get(ticketId);
}
}
/* ======== Service ======== */
class ParkingLotService{
ParkingLotRepository parkingLotRepository;
TicketRepository ticketRepository;
PricingStrategy pricingStrategy;
synchronized Ticket parkVehicle(Vehicle vehicle){
List<Floor> floors=parkingLotRepository.getFloors();
Spot spot=null;
for(Floor floor:floors){
spot=floor.findAvailableSpot(vehicle);
if(spot!=null) break;
}
if(spot==null) throw new RuntimeException("Spot not available");
spot.spotStatus=SpotStatus.Occupied;
Ticket ticket=new Ticket(vehicle,spot,LocalDateTime.now());
ticketRepository.addTicket(ticket);
return ticket;
}
synchronized boolean exitVehicle(String ticketId, PaymentMode paymentMode){
Ticket ticket=ticketRepository.getTicket(ticketId);
LocalDateTime exitTime=LocalDateTime.now();
double price=pricingStrategy.calculatePrice(ticket.vehicle,ticket.parkingTime,exitTime);
Payment payment=PaymentFactory.getPaymentMethod(paymentMode);
payment.pay(price);
ticket.exitTime=exitTime;
ticket.paymentMode=paymentMode;
ticket.amount=price;
ticket.isActive=false;
ticket.spot.spotStatus=SpotStatus.Available;
return true;
}
}
/* ======== Controller ======== */
class ParkingLotController{
ParkingLotService parkingLotService;
Ticket parkVehicle(Vehicle vehicle){
return parkingLotService.parkVehicle(vehicle);
}
boolean exitVehicle(String ticketId, PaymentMode paymentMode){
return parkingLotService.exitVehicle(ticketId,paymentMode);
}
}
/* ======== Client Code ======== */
public class ParkingLot1 {
public static void main(String[] args) {
ParkingLotController controller=new ParkingLotController();
Vehicle vehicle=VehicleFactory.getVehicle("CAR-101",VehicleType.Car);
Ticket ticket=controller.parkVehicle(vehicle);
controller.exitVehicle(ticket.id, PaymentMode.UPI);
}
}