Parking Lot

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 ?

Other Design Problems

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);
    }
}


Comments (1)