Steps in LLD Interview
Introduction: The LLD Interview Process
Section titled “Introduction: The LLD Interview Process”When faced with an LLD interview problem, it’s crucial to follow a systematic approach. Jumping straight to code is a common mistake that leads to poor designs. Instead, follow these proven steps to create clean, maintainable solutions.
The LLD Interview Flow
Section titled “The LLD Interview Flow”Step 1: Clarify Requirements
Section titled “Step 1: Clarify Requirements”Before you start designing, make sure you understand the problem!
Why This Step Matters
Section titled “Why This Step Matters”Jumping to solutions without understanding requirements leads to:
- ❌ Missing important features
- ❌ Over-engineering
- ❌ Wrong assumptions
- ❌ Wasted time
What to Ask
Section titled “What to Ask”Functional Requirements:
- What are the core features?
- What are the use cases?
- What are the constraints?
Non-Functional Requirements:
- Scale requirements?
- Performance expectations?
- Availability needs?
Edge Cases:
- What happens in error scenarios?
- What are the boundary conditions?
Interviewer: “Design a Parking Lot System”
Good Questions to Ask:
- “What types of vehicles are supported?” (Car, Motorcycle, Truck)
- “How many spots are there?” (Fixed number or configurable?)
- “Do we need to track parking duration?” (For billing)
- “What happens when the lot is full?” (Reject or waitlist?)
- “Do we need different pricing for different vehicle types?”
- “Do we need to support multiple parking lots?”
Bad Approach:
- “I’ll assume cars only and 100 spots”
- “Let me start coding right away”
Visual: Clarification Process
Section titled “Visual: Clarification Process”Step 2: Identify Actors
Section titled “Step 2: Identify Actors”Actors are the users or external systems that interact with your system.
What Are Actors?
Section titled “What Are Actors?”Actors represent:
- Users - People who use the system
- External Systems - Other systems that interact with yours
- Administrators - People who manage the system
How to Identify Actors
Section titled “How to Identify Actors”Ask yourself:
- Who will use this system?
- What are their roles?
- What actions can they perform?
Actors:
- Driver - Parks and retrieves vehicles
- Admin - Manages parking lot (add spots, view reports)
- System - Automated processes (billing, notifications)
Visual: Actors in Parking Lot System
Section titled “Visual: Actors in Parking Lot System”Common Actors in Different Systems
Section titled “Common Actors in Different Systems”| System | Actors |
|---|---|
| Library Management | Librarian, Member, System |
| Restaurant | Customer, Waiter, Chef, Manager |
| ATM | Customer, Bank, System |
| Elevator | Passenger, Maintenance, System |
Step 3: Identify Entities
Section titled “Step 3: Identify Entities”Entities are the core objects/concepts in your system that have data and behavior.
What Are Entities?
Section titled “What Are Entities?”Entities represent:
- Core Objects - Main concepts in your system
- Data Holders - Objects that store information
- Business Objects - Objects that represent business concepts
How to Identify Entities
Section titled “How to Identify Entities”Look for:
- Nouns in the problem statement
- Things that have properties
- Objects that need to be tracked
- Concepts that have state
Entities:
- ParkingLot - The parking lot itself
- ParkingSpot - Individual parking spaces
- Vehicle - Cars, motorcycles, trucks
- Ticket - Parking ticket issued to driver
- Payment - Payment transaction
Visual: Entities in Parking Lot System
Section titled “Visual: Entities in Parking Lot System”Entity vs Actor
Section titled “Entity vs Actor”Key Difference:
- Actor = External user/system that interacts with your system
- Entity = Internal object/concept within your system
Example:
- Driver (Actor) uses ParkingLot (Entity)
- Admin (Actor) manages ParkingSpot (Entity)
Each class should have a single, well-defined responsibility (Single Responsibility Principle).
Why This Step Matters
Section titled “Why This Step Matters”Proper responsibility assignment leads to:
- ✔ Maintainable code - Easy to understand and modify
- ✔ Testable code - Easy to write unit tests
- ✔ Reusable code - Classes can be reused
- ✔ SOLID principles - Follows best practices
For each entity, ask:
- What does this entity do?
- What data does it hold?
- What operations does it perform?
ParkingLot Responsibilities:
- Manage parking spots
- Assign spots to vehicles
- Find available spots
- Release spots when vehicles leave
ParkingSpot Responsibilities:
- Track its own state (occupied/available)
- Know its type (car/motorcycle/truck)
- Know its location/ID
Ticket Responsibilities:
- Store entry time
- Store vehicle information
- Calculate parking duration
Payment Responsibilities:
- Process payment
- Calculate amount based on duration
- Store payment details
Visual: Responsibility Assignment
Section titled “Visual: Responsibility Assignment”Responsibility Checklist
Section titled “Responsibility Checklist”For each class, ensure:
- Single Responsibility - One reason to change
- Cohesive - Methods are related
- Focused - Not doing too much
- Clear Purpose - Easy to understand what it does
Step 5: Design Class Diagrams
Section titled “Step 5: Design Class Diagrams”Class diagrams show the structure of your system - classes, their attributes, methods, and relationships.
Class diagrams help you:
- Visualize structure - See how classes relate
- Identify relationships - Inheritance, composition, association
- Plan implementation - Know what to code
- Communicate design - Share your design with interviewer
Key Elements of Class Diagrams
Section titled “Key Elements of Class Diagrams”- Classes - With attributes and methods
- Relationships - Inheritance, composition, association
- Interfaces - Abstract contracts
- Access Modifiers - Public, private, protected
Visual: Class Relationships
Section titled “Visual: Class Relationships”Step 6: Define Contracts & APIs
Section titled “Step 6: Define Contracts & APIs”Contracts define how classes interact - method signatures, interfaces, and API contracts.
Why Contracts Matter
Section titled “Why Contracts Matter”Well-defined contracts:
- Enable testing - Clear interfaces to mock
- Enable flexibility - Can swap implementations
- Document behavior - Clear what methods do
- Prevent errors - Compile-time checks
What to Define
Section titled “What to Define”- Method Signatures - Parameters, return types, exceptions
- Interfaces - Abstract contracts for classes
- API Contracts - If designing APIs, define endpoints
- Preconditions/Postconditions - What’s expected
Example: Parking Lot System Contracts
Section titled “Example: Parking Lot System Contracts”ParkingLot Interface:
class ParkingLot: def park_vehicle(self, vehicle: Vehicle) -> Ticket: """ Parks a vehicle and returns a ticket.
Args: vehicle: Vehicle to park
Returns: Ticket: Parking ticket with entry details
Raises: ParkingLotFullException: If no spots available InvalidVehicleException: If vehicle type not supported """ pass
def unpark_vehicle(self, ticket: Ticket) -> Payment: """ Unparks a vehicle and processes payment.
Args: ticket: Parking ticket
Returns: Payment: Payment transaction details
Raises: InvalidTicketException: If ticket is invalid """ passinterface ParkingLot { /** * Parks a vehicle and returns a ticket. * * @param vehicle Vehicle to park * @return Ticket Parking ticket with entry details * @throws ParkingLotFullException If no spots available * @throws InvalidVehicleException If vehicle type not supported */ Ticket parkVehicle(Vehicle vehicle) throws ParkingLotFullException, InvalidVehicleException;
/** * Unparks a vehicle and processes payment. * * @param ticket Parking ticket * @return Payment Payment transaction details * @throws InvalidTicketException If ticket is invalid */ Payment unparkVehicle(Ticket ticket) throws InvalidTicketException;}interface ParkingLot { /** * Parks a vehicle and returns a ticket. * * @param vehicle - Vehicle to park * @returns Parking ticket with entry details * @throws ParkingLotFullException if no spots available * @throws InvalidVehicleException if vehicle type not supported */ parkVehicle(vehicle: Vehicle): Ticket;
/** * Unparks a vehicle and processes payment. * * @param ticket - Parking ticket * @returns Payment transaction details * @throws InvalidTicketException if ticket is invalid */ unparkVehicle(ticket: Ticket): Payment;}class ParkingLot {public: /** * Parks a vehicle and returns a ticket. * * @param vehicle Vehicle to park * @return Ticket Parking ticket with entry details * @throws ParkingLotFullException If no spots available * @throws InvalidVehicleException If vehicle type not supported */ virtual Ticket parkVehicle(const Vehicle& vehicle) = 0;
/** * Unparks a vehicle and processes payment. * * @param ticket Parking ticket * @return Payment Payment transaction details * @throws InvalidTicketException If ticket is invalid */ virtual Payment unparkVehicle(const Ticket& ticket) = 0;
virtual ~ParkingLot() = default;};interface IParkingLot { /// <summary> /// Parks a vehicle and returns a ticket. /// </summary> /// <param name="vehicle">Vehicle to park</param> /// <returns>Parking ticket with entry details</returns> /// <exception cref="ParkingLotFullException">If no spots available</exception> /// <exception cref="InvalidVehicleException">If vehicle type not supported</exception> Ticket ParkVehicle(Vehicle vehicle);
/// <summary> /// Unparks a vehicle and processes payment. /// </summary> /// <param name="ticket">Parking ticket</param> /// <returns>Payment transaction details</returns> /// <exception cref="InvalidTicketException">If ticket is invalid</exception> Payment UnparkVehicle(Ticket ticket);}Payment Interface:
class PaymentProcessor: def process_payment(self, ticket: Ticket) -> Payment: """ Processes payment for parking.
Args: ticket: Parking ticket
Returns: Payment: Payment details
Raises: PaymentFailedException: If payment fails """ pass
def calculate_amount(self, ticket: Ticket) -> float: """ Calculates parking fee based on duration.
Args: ticket: Parking ticket
Returns: float: Amount to be paid """ passinterface PaymentProcessor { /** * Processes payment for parking. * * @param ticket Parking ticket * @return Payment Payment details * @throws PaymentFailedException If payment fails */ Payment processPayment(Ticket ticket) throws PaymentFailedException;
/** * Calculates parking fee based on duration. * * @param ticket Parking ticket * @return float Amount to be paid */ float calculateAmount(Ticket ticket);}interface PaymentProcessor { /** * Processes payment for parking. * * @param ticket - Parking ticket * @returns Payment details * @throws PaymentFailedException if payment fails */ processPayment(ticket: Ticket): Payment;
/** * Calculates parking fee based on duration. * * @param ticket - Parking ticket * @returns Amount to be paid */ calculateAmount(ticket: Ticket): number;}class PaymentProcessor {public: /** * Processes payment for parking. * * @param ticket Parking ticket * @return Payment Payment details * @throws PaymentFailedException If payment fails */ virtual Payment processPayment(const Ticket& ticket) = 0;
/** * Calculates parking fee based on duration. * * @param ticket Parking ticket * @return float Amount to be paid */ virtual float calculateAmount(const Ticket& ticket) = 0;
virtual ~PaymentProcessor() = default;};interface IPaymentProcessor { /// <summary> /// Processes payment for parking. /// </summary> /// <param name="ticket">Parking ticket</param> /// <returns>Payment details</returns> /// <exception cref="PaymentFailedException">If payment fails</exception> Payment ProcessPayment(Ticket ticket);
/// <summary> /// Calculates parking fee based on duration. /// </summary> /// <param name="ticket">Parking ticket</param> /// <returns>Amount to be paid</returns> float CalculateAmount(Ticket ticket);}Visual: API Contracts
Section titled “Visual: API Contracts”Step 7: Handle Edge Cases
Section titled “Step 7: Handle Edge Cases”Edge cases are scenarios that might not be obvious but are important to handle.
Why Edge Cases Matter
Section titled “Why Edge Cases Matter”Handling edge cases shows:
- Mature thinking - You consider all scenarios
- Production readiness - Real systems have edge cases
- Attention to detail - You think thoroughly
- Error handling - You plan for failures
Common Edge Cases
Section titled “Common Edge Cases”- Null/Empty Inputs - What if input is null or empty?
- Boundary Conditions - What if at limits?
- Concurrent Access - What if multiple users?
- Error Scenarios - What if something fails?
- Invalid States - What if system is in wrong state?
Example: Parking Lot System Edge Cases
Section titled “Example: Parking Lot System Edge Cases”Edge Cases to Handle:
-
Parking lot is full
- Return appropriate exception
- Don’t crash
-
Invalid vehicle type
- Validate vehicle type
- Return clear error message
-
Invalid ticket
- Validate ticket exists
- Check ticket hasn’t been used
-
Concurrent parking
- Handle race conditions
- Use locks/synchronization
-
Payment failure
- Handle payment errors
- Don’t release spot if payment fails
Visual: Edge Case Handling
Section titled “Visual: Edge Case Handling”Step 8: Code Implementation
Section titled “Step 8: Code Implementation”Finally, implement your design in code (or pseudo-code).
Implementation Guidelines
Section titled “Implementation Guidelines”- Follow your design - Implement what you designed
- Clean code - Readable, maintainable
- SOLID principles - Apply what you learned
- Design patterns - Use appropriate patterns
- Error handling - Handle edge cases
Example: Parking Lot System Implementation
Section titled “Example: Parking Lot System Implementation”from abc import ABC, abstractmethodfrom datetime import datetimefrom enum import Enumfrom typing import Optional, List
class VehicleType(Enum): CAR = "CAR" MOTORCYCLE = "MOTORCYCLE" TRUCK = "TRUCK"
class Vehicle(ABC): def __init__(self, license_plate: str): self.license_plate = license_plate
@abstractmethod def get_vehicle_type(self) -> VehicleType: pass
class Car(Vehicle): def get_vehicle_type(self) -> VehicleType: return VehicleType.CAR
class ParkingSpot: def __init__(self, spot_id: str, spot_type: VehicleType): self.spot_id = spot_id self.spot_type = spot_type self.is_occupied = False self.vehicle: Optional[Vehicle] = None
def park_vehicle(self, vehicle: Vehicle) -> None: if not self.is_available(): raise ValueError("Spot is already occupied") if vehicle.get_vehicle_type() != self.spot_type: raise ValueError("Vehicle type doesn't match spot type")
self.vehicle = vehicle self.is_occupied = True
def unpark_vehicle(self) -> Vehicle: if not self.is_occupied: raise ValueError("Spot is not occupied")
vehicle = self.vehicle self.vehicle = None self.is_occupied = False return vehicle
def is_available(self) -> bool: return not self.is_occupied
class Ticket: def __init__(self, ticket_id: str, vehicle: Vehicle, spot: ParkingSpot): self.ticket_id = ticket_id self.vehicle = vehicle self.spot = spot self.entry_time = datetime.now()
def calculate_duration(self) -> float: return (datetime.now() - self.entry_time).total_seconds() / 3600
class ParkingLot: def __init__(self, capacity: int): self.capacity = capacity self.spots: List[ParkingSpot] = [] self.tickets: dict[str, Ticket] = {}
def park_vehicle(self, vehicle: Vehicle) -> Ticket: # Find available spot spot = self._find_available_spot(vehicle.get_vehicle_type()) if not spot: raise ValueError("Parking lot is full")
# Park vehicle spot.park_vehicle(vehicle)
# Create ticket ticket_id = f"TICKET_{len(self.tickets) + 1}" ticket = Ticket(ticket_id, vehicle, spot) self.tickets[ticket_id] = ticket
return ticket
def unpark_vehicle(self, ticket_id: str) -> float: if ticket_id not in self.tickets: raise ValueError("Invalid ticket")
ticket = self.tickets[ticket_id] ticket.spot.unpark_vehicle()
# Calculate payment duration = ticket.calculate_duration() amount = duration * 10 # $10 per hour
del self.tickets[ticket_id] return amount
def _find_available_spot(self, vehicle_type: VehicleType) -> Optional[ParkingSpot]: for spot in self.spots: if spot.spot_type == vehicle_type and spot.is_available(): return spot return Noneimport java.time.LocalDateTime;import java.time.Duration;import java.util.*;
enum VehicleType { CAR, MOTORCYCLE, TRUCK}
abstract class Vehicle { protected String licensePlate;
public Vehicle(String licensePlate) { this.licensePlate = licensePlate; }
public abstract VehicleType getVehicleType();}
class Car extends Vehicle { public Car(String licensePlate) { super(licensePlate); }
@Override public VehicleType getVehicleType() { return VehicleType.CAR; }}
class ParkingSpot { private String spotId; private VehicleType spotType; private boolean isOccupied; private Vehicle vehicle;
public ParkingSpot(String spotId, VehicleType spotType) { this.spotId = spotId; this.spotType = spotType; this.isOccupied = false; }
public void parkVehicle(Vehicle vehicle) { if (!isAvailable()) { throw new IllegalStateException("Spot is already occupied"); } if (vehicle.getVehicleType() != spotType) { throw new IllegalArgumentException("Vehicle type doesn't match spot type"); }
this.vehicle = vehicle; this.isOccupied = true; }
public Vehicle unparkVehicle() { if (!isOccupied) { throw new IllegalStateException("Spot is not occupied"); }
Vehicle parkedVehicle = vehicle; this.vehicle = null; this.isOccupied = false; return parkedVehicle; }
public boolean isAvailable() { return !isOccupied; }
public VehicleType getSpotType() { return spotType; }}
class Ticket { private String ticketId; private Vehicle vehicle; private ParkingSpot spot; private LocalDateTime entryTime;
public Ticket(String ticketId, Vehicle vehicle, ParkingSpot spot) { this.ticketId = ticketId; this.vehicle = vehicle; this.spot = spot; this.entryTime = LocalDateTime.now(); }
public double calculateDuration() { return Duration.between(entryTime, LocalDateTime.now()).toHours(); }
public ParkingSpot getSpot() { return spot; }}
class ParkingLot { private int capacity; private List<ParkingSpot> spots; private Map<String, Ticket> tickets;
public ParkingLot(int capacity) { this.capacity = capacity; this.spots = new ArrayList<>(); this.tickets = new HashMap<>(); }
public Ticket parkVehicle(Vehicle vehicle) { ParkingSpot spot = findAvailableSpot(vehicle.getVehicleType()); if (spot == null) { throw new IllegalStateException("Parking lot is full"); }
spot.parkVehicle(vehicle);
String ticketId = "TICKET_" + (tickets.size() + 1); Ticket ticket = new Ticket(ticketId, vehicle, spot); tickets.put(ticketId, ticket);
return ticket; }
public double unparkVehicle(String ticketId) { Ticket ticket = tickets.get(ticketId); if (ticket == null) { throw new IllegalArgumentException("Invalid ticket"); }
ticket.getSpot().unparkVehicle();
double duration = ticket.calculateDuration(); double amount = duration * 10; // $10 per hour
tickets.remove(ticketId); return amount; }
private ParkingSpot findAvailableSpot(VehicleType vehicleType) { return spots.stream() .filter(spot -> spot.getSpotType() == vehicleType && spot.isAvailable()) .findFirst() .orElse(null); }}enum VehicleType { CAR = "CAR", MOTORCYCLE = "MOTORCYCLE", TRUCK = "TRUCK"}
abstract class Vehicle { protected licensePlate: string;
constructor(licensePlate: string) { this.licensePlate = licensePlate; }
abstract getVehicleType(): VehicleType;}
class Car extends Vehicle { getVehicleType(): VehicleType { return VehicleType.CAR; }}
class ParkingSpot { private spotId: string; private spotType: VehicleType; private occupied: boolean; private vehicle: Vehicle | null;
constructor(spotId: string, spotType: VehicleType) { this.spotId = spotId; this.spotType = spotType; this.occupied = false; this.vehicle = null; }
parkVehicle(vehicle: Vehicle): void { if (!this.isAvailable()) { throw new Error("Spot is already occupied"); } if (vehicle.getVehicleType() !== this.spotType) { throw new Error("Vehicle type doesn't match spot type"); }
this.vehicle = vehicle; this.occupied = true; }
unparkVehicle(): Vehicle { if (!this.occupied || !this.vehicle) { throw new Error("Spot is not occupied"); }
const vehicle = this.vehicle; this.vehicle = null; this.occupied = false; return vehicle; }
isAvailable(): boolean { return !this.occupied; }
getSpotType(): VehicleType { return this.spotType; }}
class Ticket { private ticketId: string; private vehicle: Vehicle; private parkingSpot: ParkingSpot; private entryTime: Date;
constructor(ticketId: string, vehicle: Vehicle, spot: ParkingSpot) { this.ticketId = ticketId; this.vehicle = vehicle; this.parkingSpot = spot; this.entryTime = new Date(); }
calculateDuration(): number { const now = new Date(); return (now.getTime() - this.entryTime.getTime()) / (1000 * 60 * 60); }
getSpot(): ParkingSpot { return this.parkingSpot; }}
class ParkingLot { private capacity: number; private spots: ParkingSpot[]; private tickets: Map<string, Ticket>;
constructor(capacity: number) { this.capacity = capacity; this.spots = []; this.tickets = new Map(); }
parkVehicle(vehicle: Vehicle): Ticket { const spot = this.findAvailableSpot(vehicle.getVehicleType()); if (!spot) { throw new Error("Parking lot is full"); }
spot.parkVehicle(vehicle);
const ticketId = `TICKET_${this.tickets.size + 1}`; const ticket = new Ticket(ticketId, vehicle, spot); this.tickets.set(ticketId, ticket);
return ticket; }
unparkVehicle(ticketId: string): number { const ticket = this.tickets.get(ticketId); if (!ticket) { throw new Error("Invalid ticket"); }
ticket.getSpot().unparkVehicle();
const duration = ticket.calculateDuration(); const amount = duration * 10; // $10 per hour
this.tickets.delete(ticketId); return amount; }
private findAvailableSpot(vehicleType: VehicleType): ParkingSpot | null { for (const spot of this.spots) { if (spot.getSpotType() === vehicleType && spot.isAvailable()) { return spot; } } return null; }}#include <string>#include <vector>#include <map>#include <memory>#include <chrono>#include <stdexcept>#include <optional>
enum class VehicleType { CAR, MOTORCYCLE, TRUCK};
class Vehicle {protected: std::string licensePlate;
public: Vehicle(const std::string& licensePlate) : licensePlate(licensePlate) {} virtual ~Vehicle() = default; virtual VehicleType getVehicleType() const = 0;};
class Car : public Vehicle {public: Car(const std::string& licensePlate) : Vehicle(licensePlate) {} VehicleType getVehicleType() const override { return VehicleType::CAR; }};
class ParkingSpot {private: std::string spotId; VehicleType spotType; bool occupied; Vehicle* vehicle;
public: ParkingSpot(const std::string& spotId, VehicleType spotType) : spotId(spotId), spotType(spotType), occupied(false), vehicle(nullptr) {}
void parkVehicle(Vehicle* v) { if (!isAvailable()) { throw std::runtime_error("Spot is already occupied"); } if (v->getVehicleType() != spotType) { throw std::invalid_argument("Vehicle type doesn't match spot type"); }
vehicle = v; occupied = true; }
Vehicle* unparkVehicle() { if (!occupied) { throw std::runtime_error("Spot is not occupied"); }
Vehicle* v = vehicle; vehicle = nullptr; occupied = false; return v; }
bool isAvailable() const { return !occupied; }
VehicleType getSpotType() const { return spotType; }};
class Ticket {private: std::string ticketId; Vehicle* vehicle; ParkingSpot* spot; std::chrono::system_clock::time_point entryTime;
public: Ticket(const std::string& ticketId, Vehicle* vehicle, ParkingSpot* spot) : ticketId(ticketId), vehicle(vehicle), spot(spot), entryTime(std::chrono::system_clock::now()) {}
double calculateDuration() const { auto now = std::chrono::system_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::hours>(now - entryTime); return duration.count(); }
ParkingSpot* getSpot() const { return spot; }};
class ParkingLot {private: int capacity; std::vector<ParkingSpot*> spots; std::map<std::string, Ticket*> tickets;
ParkingSpot* findAvailableSpot(VehicleType vehicleType) { for (auto spot : spots) { if (spot->getSpotType() == vehicleType && spot->isAvailable()) { return spot; } } return nullptr; }
public: ParkingLot(int capacity) : capacity(capacity) {}
Ticket* parkVehicle(Vehicle* vehicle) { ParkingSpot* spot = findAvailableSpot(vehicle->getVehicleType()); if (!spot) { throw std::runtime_error("Parking lot is full"); }
spot->parkVehicle(vehicle);
std::string ticketId = "TICKET_" + std::to_string(tickets.size() + 1); Ticket* ticket = new Ticket(ticketId, vehicle, spot); tickets[ticketId] = ticket;
return ticket; }
double unparkVehicle(const std::string& ticketId) { auto it = tickets.find(ticketId); if (it == tickets.end()) { throw std::invalid_argument("Invalid ticket"); }
Ticket* ticket = it->second; ticket->getSpot()->unparkVehicle();
double duration = ticket->calculateDuration(); double amount = duration * 10; // $10 per hour
tickets.erase(it); return amount; }
~ParkingLot() { for (auto& pair : tickets) { delete pair.second; } }};using System;using System.Collections.Generic;using System.Linq;
enum VehicleType { CAR, MOTORCYCLE, TRUCK}
abstract class Vehicle { protected string licensePlate;
public Vehicle(string licensePlate) { this.licensePlate = licensePlate; }
public abstract VehicleType GetVehicleType();}
class Car : Vehicle { public Car(string licensePlate) : base(licensePlate) {}
public override VehicleType GetVehicleType() { return VehicleType.CAR; }}
class ParkingSpot { private string spotId; private VehicleType spotType; private bool isOccupied; private Vehicle vehicle;
public ParkingSpot(string spotId, VehicleType spotType) { this.spotId = spotId; this.spotType = spotType; this.isOccupied = false; }
public void ParkVehicle(Vehicle vehicle) { if (!IsAvailable()) { throw new InvalidOperationException("Spot is already occupied"); } if (vehicle.GetVehicleType() != spotType) { throw new ArgumentException("Vehicle type doesn't match spot type"); }
this.vehicle = vehicle; this.isOccupied = true; }
public Vehicle UnparkVehicle() { if (!isOccupied) { throw new InvalidOperationException("Spot is not occupied"); }
Vehicle parkedVehicle = vehicle; this.vehicle = null; this.isOccupied = false; return parkedVehicle; }
public bool IsAvailable() { return !isOccupied; }
public VehicleType GetSpotType() { return spotType; }}
class Ticket { private string ticketId; private Vehicle vehicle; private ParkingSpot spot; private DateTime entryTime;
public Ticket(string ticketId, Vehicle vehicle, ParkingSpot spot) { this.ticketId = ticketId; this.vehicle = vehicle; this.spot = spot; this.entryTime = DateTime.Now; }
public double CalculateDuration() { return (DateTime.Now - entryTime).TotalHours; }
public ParkingSpot GetSpot() { return spot; }}
class ParkingLot { private int capacity; private List<ParkingSpot> spots; private Dictionary<string, Ticket> tickets;
public ParkingLot(int capacity) { this.capacity = capacity; this.spots = new List<ParkingSpot>(); this.tickets = new Dictionary<string, Ticket>(); }
public Ticket ParkVehicle(Vehicle vehicle) { ParkingSpot spot = FindAvailableSpot(vehicle.GetVehicleType()); if (spot == null) { throw new InvalidOperationException("Parking lot is full"); }
spot.ParkVehicle(vehicle);
string ticketId = $"TICKET_{tickets.Count + 1}"; Ticket ticket = new Ticket(ticketId, vehicle, spot); tickets[ticketId] = ticket;
return ticket; }
public double UnparkVehicle(string ticketId) { if (!tickets.ContainsKey(ticketId)) { throw new ArgumentException("Invalid ticket"); }
Ticket ticket = tickets[ticketId]; ticket.GetSpot().UnparkVehicle();
double duration = ticket.CalculateDuration(); double amount = duration * 10; // $10 per hour
tickets.Remove(ticketId); return amount; }
private ParkingSpot FindAvailableSpot(VehicleType vehicleType) { return spots.FirstOrDefault(spot => spot.GetSpotType() == vehicleType && spot.IsAvailable()); }}Complete Process Flow
Section titled “Complete Process Flow”Visual Summary of All Steps
Section titled “Visual Summary of All Steps”Step-by-Step Checklist
Section titled “Step-by-Step Checklist”Use this checklist for every LLD interview:
- Step 1: Clarify requirements - Ask questions
- Step 2: Identify actors - Who uses the system?
- Step 3: Identify entities - What are the core objects?
- Step 4: Assign responsibilities - What does each class do?
- Step 5: Design class diagrams - Show relationships
- Step 6: Define contracts & APIs - Method signatures
- Step 7: Handle edge cases - Error scenarios
- Step 8: Code implementation - Write clean code
Next Steps: Deep Dive into Each Step
Section titled “Next Steps: Deep Dive into Each Step”Now that you understand the high-level process, let’s dive deep into each step:
Next: Identifying Actors & Entities →
This next guide will teach you how to systematically identify actors and entities with detailed examples and visual diagrams!
Summary
Section titled “Summary”Key Takeaways
Section titled “Key Takeaways”✔ Follow a systematic approach - Don’t jump to code
✔ Clarify requirements first - Ask questions
✔ Identify actors - Who uses the system?
✔ Identify entities - What are the core objects?
✔ Assign responsibilities - Single Responsibility Principle
✔ Design class diagrams - Visualize structure
✔ Define contracts - Clear interfaces
✔ Handle edge cases - Think about errors
✔ Implement cleanly - Follow your design
Remember
Section titled “Remember”LLD interviews are about demonstrating:
- Systematic thinking
- Clean design skills
- Communication ability
- Production-ready mindset
Master these steps, and you’ll excel in any LLD interview! 🚀