Bird
Raised Fist0

SOLID Principles - Single Responsibility to Dependency Inversion with Real Code

LLDeasyInterview freq: high
Choose your preparation mode4 modes available
๐ŸŽฏ
SOLID Principles - Single Responsibility to Dependency Inversion with Real Code
40 min ยท medium
medium

Design a parking lot system with 5 levels and 4 gates. Each level has multiple parking spots of different types (compact, large, handicapped). Vehicles of various types (car, truck, motorcycle) arrive and park. The system calculates parking fees based on duration and spot type. It must handle concurrent vehicle entries and exits.

  • ParkingLot has 5 Levels, each with multiple SpotTypes (COMPACT, LARGE, HANDICAPPED).
  • 4 Gates handle vehicle entry and exit concurrently.
  • Pricing depends on SpotType and parking duration.
  • Vehicles have types (CAR, TRUCK, MOTORCYCLE) that determine eligible SpotTypes.
  • Concurrency: multiple threads may park or unpark vehicles simultaneously.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Vehicle and SpotType using enums and abstract class
Design Vehicle hierarchy and SpotType enum to represent vehicle and spot categories. Vehicle should hold licensePlate and vehicleType state.
๐Ÿ’ก Hint
Use enum for VehicleType and SpotType to ensure type safety and exhaustive switch. Use abstract class Vehicle to hold licensePlate and vehicleType fields because interfaces cannot hold state in Java.
abstract class Vehicle(licensePlate:String, vehicleType:VehicleType) - abstract because it holds instance state; VehicleType and SpotType as enums for compile-time safety
Mid: Recognizes abstract class for shared state and enums for type safety. Senior: Explains interface cannot hold fields in Java, enums prevent runtime typo bugs and enable exhaustive switch.
โš ๏ธCommon Wrong Answer
Using String for VehicleType/SpotType leads to runtime typo bugs and no compile-time exhaustive switch checking.
Step 2
Design ParkingSpot and Level with composition and synchronized availability
Model ParkingSpot with spotType and availability status. Level contains multiple ParkingSpots. Ensure thread-safe parking spot allocation.
๐Ÿ’ก Hint
Use composition: Level owns ParkingSpots lifecycle. Use volatile boolean isAvailable in ParkingSpot and synchronized boolean parkVehicle(Vehicle v) to atomically check-and-set availability.
ParkingSpot has volatile boolean isAvailable; synchronized parkVehicle(Vehicle v) ensures atomic check-and-set to prevent race conditions during concurrent parking attempts.
Mid: Uses synchronized method to protect availability state. Senior: Traces thread interleaving where Thread 1 reads isAvailable=true, context switch, Thread 2 reads isAvailable=true, both park causing double booking without synchronization.
โš ๏ธCommon Wrong Answer
Not synchronizing parkVehicle leads to race conditions where multiple threads park on the same spot simultaneously.
Step 3
Implement PricingStrategy interface and concrete classes for pricing
Design pricing calculation using Dependency Inversion. PricingStrategy interface defines calculateFee. Implement concrete strategies for different spot types.
๐Ÿ’ก Hint
Use interface PricingStrategy with method calculateFee(long durationMinutes). Inject PricingStrategy into ParkingLot to follow Dependency Inversion Principle.
PricingStrategy interface with calculateFee(long durationMinutes) method; concrete classes implement pricing logic. ParkingLot depends on PricingStrategy abstraction, not concrete implementations.
Mid: Identifies Dependency Inversion by coding to interface. Senior: Explains how this enables swapping pricing logic without modifying ParkingLot, adhering to Open/Closed Principle.
โš ๏ธCommon Wrong Answer
Hardcoding pricing logic inside ParkingLot violates Dependency Inversion and Open/Closed principles, making future changes error-prone.
Step 4
Design Gate class handling concurrent vehicle entry and exit
Model Gate as an entry/exit point that interacts with ParkingLot to park or unpark vehicles concurrently.
๐Ÿ’ก Hint
Gate holds reference to ParkingLot (aggregation). Use synchronized methods in ParkingLot for thread-safe park/unpark operations. Gate delegates calls to ParkingLot.
Gate aggregates ParkingLot; calls synchronized parkVehicle/unparkVehicle methods to ensure thread-safe operations during concurrent vehicle processing.
Mid: Uses synchronized methods in ParkingLot for concurrency. Senior: Explains how synchronized parkVehicle/unparkVehicle prevent inconsistent spot states when multiple Gates operate concurrently.
โš ๏ธCommon Wrong Answer
Allowing Gate to modify ParkingSpot availability without synchronization causes inconsistent spot states and data races.
Step 5
Apply Single Responsibility and Open/Closed principles in class design
Ensure each class has a single responsibility and is open for extension but closed for modification.
๐Ÿ’ก Hint
Separate concerns: Vehicle hierarchy only models vehicles, PricingStrategy only calculates fees, ParkingLot manages spots and vehicles. Use interfaces and abstract classes to enable extension without modifying existing code.
Each class has focused responsibility; PricingStrategy interface enables extension without modifying ParkingLot; Vehicle hierarchy uses abstract class for shared state, enabling extension.
Mid: Identifies SRP and OCP in class separation. Senior: Explains how abstract classes and interfaces enable extension without code changes, reducing regression risk.
โš ๏ธCommon Wrong Answer
Combining pricing logic inside ParkingLot violates SRP and OCP, making the system fragile and hard to extend.
๐Ÿ”Œ
API Design Challenge
Design a thread-safe API for parking and unparking vehicles in a multi-level parking lot with multiple gates. The API should allow querying spot availability, parking a vehicle, unparking a vehicle, and calculating fees based on spot type and duration.
  • Use synchronized methods or locks to ensure thread safety during park/unpark operations.
  • Design interfaces for pricing strategies to allow flexible fee calculations.
  • Use enums for vehicle and spot types to enforce type safety and simplify logic.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • abstract class Vehicle with instance state and VehicleType enum
  • Enums for VehicleType and SpotType to prevent runtime bugs
  • volatile and synchronized in ParkingSpot for concurrency correctness
  • PricingStrategy interface for Dependency Inversion
  • Synchronized parkVehicle/unparkVehicle methods in ParkingLot
  • Composition relationship between Level and ParkingSpot
Good to Have - 30%
  • Aggregation relationship between Gate and ParkingLot
  • Use of double-checked locking with volatile singleton instance for ParkingLot if singleton pattern applied
  • Exhaustive switch statements on enums to prevent missing cases
Bonus - 10%
    โœ…
    Model Answer
    โ–ผ Reveal

    This design models a parking lot system applying SOLID principles with Java-specific constructs. Vehicles and spot types are represented by enums and an abstract Vehicle class holding state. ParkingSpot uses volatile and synchronized methods to ensure thread-safe availability updates. Levels compose ParkingSpots, owning their lifecycle. PricingStrategy interface abstracts fee calculation, enabling dependency inversion. Gates aggregate ParkingLot and delegate synchronized park/unpark calls to handle concurrency. The design enforces Single Responsibility and Open/Closed principles by separating concerns and using interfaces and abstract classes for extensibility.

    • Use abstract class Vehicle to hold instance state because Java interfaces cannot hold fields
    • Use enums for VehicleType and SpotType to ensure compile-time safety and enable exhaustive switch statements
    • Use volatile boolean isAvailable in ParkingSpot and synchronized parkVehicle method to prevent race conditions during concurrent parking
    • Level owns ParkingSpots lifecycle via composition to encapsulate spot management
    • Define PricingStrategy interface to abstract fee calculation and inject into ParkingLot to follow Dependency Inversion Principle
    • Synchronize parkVehicle and unparkVehicle methods in ParkingLot to ensure thread safety when multiple Gates operate concurrently
    • Gate aggregates ParkingLot and delegates synchronized park/unpark calls to maintain consistent spot states
    • Apply Single Responsibility Principle by separating vehicle modeling, pricing, parking management, and gate operations
    • Apply Open/Closed Principle by using interfaces and abstract classes to enable extension without modifying existing code
    • If ParkingLot is implemented as singleton, use double-checked locking with volatile instance to prevent partially constructed object visibility
    • abstract class Vehicle holds licensePlate and VehicleType enum - interfaces cannot hold instance state in Java
    • Enums VehicleType and SpotType ensure compile-time safety and exhaustive switch statements
    • ParkingSpot uses volatile boolean isAvailable and synchronized parkVehicle() for atomic availability updates
    • Level composes ParkingSpots; lifecycle owned by Level (composition)
    • PricingStrategy interface abstracts fee calculation; ParkingLot depends on interface (Dependency Inversion Principle)
    • Gate aggregates ParkingLot and delegates synchronized park/unpark calls to ensure thread safety
    • Synchronized methods in ParkingLot prevent race conditions during concurrent vehicle parking/unparking
    • Single Responsibility Principle enforced by separating Vehicle, Pricing, ParkingLot, and Gate responsibilities
    • Open/Closed Principle enabled by abstract classes and interfaces allowing extension without modification
    • Double-checked locking with volatile singleton instance (if ParkingLot singleton used) to prevent partially constructed object visibility

    Practice

    (1/5)
    1. In the Elevator System, should the Elevator class implement an interface or extend an abstract class to represent different elevator types (e.g., passenger, freight)? Consider that elevators share common state and behavior but also have type-specific logic.
    easy
    A. Use an abstract class, because elevators share common state and partial behavior implementations.
    B. Use an interface, because elevators only share behavior signatures, not state.
    C. Use a concrete class, because inheritance is unnecessary for elevator types.
    D. Use multiple interfaces, one per behavior, to avoid state duplication.

    Solution

    1. Step 1: Analyze shared state and behavior

      Elevators share common fields like currentFloor, direction, and doorStatus, plus some implemented methods.
    2. Step 2: Abstract class fits shared state and partial behavior

      Abstract class allows code reuse and enforces common state, while subclasses override type-specific logic.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      Abstract class supports shared state and behavior [check]
    Hint: Abstract class for shared state; interface for pure behavior [check]
    Common Mistakes:
    2. In designing the ESPNcricinfo system, should the Ball entity be modeled as an abstract class or an interface? Consider that Ball has state like ballNumber and behavior like calculateRuns(). Which is the best choice and why?
    easy
    A. Abstract class, because Ball has shared state and behavior that subclasses inherit.
    B. Interface, because Ball only defines behavior and no state should be stored.
    C. Interface, because Java interfaces can now have default methods with state variables.
    D. Abstract class, because it allows multiple inheritance of Ball features.

    Solution

    1. Step 1: Analyze Ball's state and behavior

      Ball has state (ballNumber) and behavior (calculateRuns), so it needs to store data and provide common logic.
    2. Step 2: Abstract class vs interface for state

      Interfaces cannot hold instance state; abstract classes can hold state and provide shared implementation.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      Stateful entity with behavior -> abstract class [check]
    Hint: Stateful with behavior -> abstract class, stateless behavior -> interface [check]
    Common Mistakes:
    3. In designing a Vehicle abstraction for a parking system, should Vehicle be an abstract class or an interface if it needs to hold state like licensePlate? Why?
    easy
    A. Interface, because it enforces method signatures without implementation or state, which is ideal for all vehicles.
    B. Abstract class, because it forces all subclasses to implement all methods explicitly.
    C. Interface, because Java 8+ allows default methods that can hold state variables safely.
    D. Abstract class, because it can hold shared state like licensePlate and provide common behavior implementations.

    Solution

    1. Step 1: Identify state holding needs

      licensePlate is shared state, which interfaces cannot hold as fields.
    2. Step 2: Evaluate options

      Abstract classes can hold state and provide default behavior; interfaces cannot hold instance fields, only constants.
    3. Final Answer:

      Option D -> Option D
    4. Quick Check:

      Abstract class holds shared state like licensePlate [check]
    Hint: State needs abstract class, interfaces only declare behavior [check]
    Common Mistakes:
    4. In designing an Elevator system using the State pattern, should the ElevatorState be an abstract class or an interface, and why?
    easy
    A. Interface, because states only define behavior without shared implementation
    B. Abstract class, because states share common code and state variables
    C. Abstract class, because it enforces a single inheritance hierarchy
    D. Interface, because it allows multiple inheritance of state behaviors

    Solution

    1. Step 1: Analyze shared code in states

      Elevator states often share common fields (e.g., reference to context) and helper methods, which favors an abstract class.
    2. Step 2: Evaluate interface limitations

      Interfaces cannot hold state or provide default implementations (prior to Java 8), so they are less suitable if shared code exists.
    3. Final Answer:

      Option B -> Option B
    4. Quick Check:

      Shared code and state -> abstract class [check]
    Hint: Shared code and state -> abstract class; behavior only -> interface [check]
    Common Mistakes:
    5. You want to add a WeekendFeeStrategy to calculate parking fees on weekends without modifying existing classes. Which SOLID principle guides this design and what is the minimum change?
    medium
    A. Add WeekendFeeStrategy implementing FeeStrategy and inject it, preserving OCP and DIP
    B. Create a new subclass WeekendParkingLot duplicating most logic, violating DRY
    C. Add a boolean flag in Vehicle to indicate weekend parking, violating SRP
    D. Modify ParkingLot to add weekend logic directly, violating OCP but simpler

    Solution

    1. Step 1: Identify applicable SOLID principles

      Open/Closed Principle (OCP) requires extending behavior without modifying existing code; Dependency Inversion Principle (DIP) requires depending on abstractions like FeeStrategy.
    2. Step 2: Apply minimal change preserving OCP and DIP

      Implement WeekendFeeStrategy as a new class implementing FeeStrategy and inject it where needed, avoiding changes to existing classes.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      New strategy class + injection -> OCP and DIP preserved [check]
    Hint: New strategy class + inject -> OCP and DIP preserved [check]
    Common Mistakes:
    Recommended

    Ready to prove it?

    Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

    10 questions~5 mineasy hard