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
Demonstrate mastery of SOLID principles in Java with concrete classes, interfaces, and concurrency-safe methods.
easyOOP Foundations for InterviewsFreq: high
Prerequisites:OOP FundamentalsJava concurrency basicsInterfaces and abstract classes in Java
⚙️
Functional Requirements
  • Classes must encapsulate a single responsibility
  • Interfaces must be used to define contracts without state
  • Abstract classes must be used when shared state or behavior is needed
  • Dependency inversion must be demonstrated via constructor injection
  • Concurrency control must prevent race conditions on shared mutable fields
  • Code must be extensible without modifying existing classes (Open-Closed Principle)
  • Use Java-specific concurrency primitives (volatile, synchronized) appropriately
👤
Use Cases
  • Define a class with a single responsibility
  • Refactor code to separate concerns using interfaces and abstract classes
  • Implement dependency injection to invert dependencies
  • Ensure thread-safe access to shared mutable state
  • Demonstrate open-closed principle by extending behavior without modifying existing classes
🧑‍💻
Actors
  • Developer
  • Code Reviewer
  • Build System
  • Runtime JVM
❓
Clarifying Questions to Ask
  • How many threads will concurrently access shared objects?
  • Should dependency injection be manual or use a framework?
  • Are there any performance constraints on synchronization?
  • What is the expected lifecycle of injected dependencies?
  • How should edge cases like null dependencies or concurrent modification be handled?
Out of Scope
✗ UI design
✗ Persistence or database integration
✗ Network communication
✗ Advanced concurrency utilities beyond synchronized and volatile
⚡
QPS Calculation
write qps1000
read qps5000
🖥️
Server Estimate
3
Estimates based on typical interview coding exercise scale; servers sized to handle concurrency and throughput.
🔢
Enumerations
enum ResponsibilityType
SINGLE | MULTIPLE
Defines whether a class adheres to single responsibility or violates it, enabling exhaustive checks and clear documentation.
enum DependencyType
CONCRETE | ABSTRACTION
Distinguishes between concrete class dependencies and abstraction dependencies to enforce Dependency Inversion Principle.
📦
SingleResponsibilityClass
2 fields
private String nameprivate ResponsibilityType responsibilityType
Encapsulates a single responsibility with no side effects; demonstrates Single Responsibility Principle.
Why thisUsing a dedicated class for single responsibility ensures maintainability and testability, aligning with SOLID principles.
Why notAvoid using monolithic classes that combine multiple responsibilities, which complicate testing and maintenance.
public String getName()Returns the name of the class.
public void performAction()Performs the single responsibility action.
📦
AbstractBaseClass
1 fields
protected volatile boolean initialized
Provides shared state and behavior for subclasses; demonstrates why abstract class is needed over interface due to state.
Why thisAbstract class allows shared state and behavior, which interfaces cannot provide, enabling code reuse and controlled initialization.
Why notInterfaces cannot hold state; using abstract class avoids code duplication and supports synchronized initialization.
public synchronized void initialize()Initializes shared state atomically, ensuring visibility with volatile.
public boolean isInitialized()Returns whether the object has been initialized, reading volatile field.
📦
DependencyInterface
0 fields
Defines a contract without state; used to invert dependencies and enable polymorphism.
Why thisInterfaces define clear contracts enabling loose coupling and easier testing via mocks or stubs.
Why notAvoid concrete class dependencies to prevent tight coupling and reduce flexibility.
public void execute()Abstract method to be implemented by concrete dependencies.
📦
ConcreteDependency
1 fields
private String resource
Implements DependencyInterface; represents a concrete dependency injected into clients.
Why thisConcrete implementation encapsulates specific resource handling, allowing clients to depend on abstractions.
Why notAvoid embedding resource logic directly in clients to maintain separation of concerns.
public void execute()Performs concrete dependency action.
📦
ClientClass
1 fields
private final DependencyInterface dependency
Depends on abstraction (DependencyInterface) rather than concrete class; demonstrates Dependency Inversion Principle.
Why thisConstructor injection ensures immutability and clear dependency management, facilitating testing and flexibility.
Why notAvoid creating dependencies inside the class to prevent tight coupling and hinder testing.
public ClientClass(DependencyInterface dependency)Constructor injection of dependency.
public void performTask()Uses injected dependency to perform task.
🔗
Relationships
ClientClass──►DependencyInterface[1]
associationDependencyInterface can exist independently of ClientClass
ConcreteDependency──►DependencyInterface[1]
realizationConcreteDependency implements DependencyInterface
AbstractBaseClass──►SingleResponsibilityClass[1]
inheritanceSingleResponsibilityClass extends AbstractBaseClass
ClientClass──►ConcreteDependency[1]
associationConcreteDependency injected into ClientClass but can exist independently
🗄️
Classes
PK: classId
classId UUIDname StringresponsibilityType ResponsibilityType
Stores class metadata including responsibility type for SOLID principle validation.
🗄️
Dependencies
PK: dependencyId
dependencyId UUIDname StringdependencyType DependencyType
Stores dependency details distinguishing concrete and abstraction types.
🔌
CreateClass
Request
{
  "name": "String",
  "responsibilityType": "ResponsibilityType"
}
Response
{
  "classId": "UUID",
  "status": "String"
}
Creates a new class entry enforcing single responsibility.
🔌
InjectDependency
Request
{
  "clientClassId": "UUID",
  "dependencyId": "UUID"
}
Response
{
  "status": "String"
}
Injects a dependency into a client class demonstrating Dependency Inversion.
🔌
CheckInitialization
Request
{
  "classId": "UUID"
}
Response
{
  "initialized": "boolean"
}
Checks if a class has been initialized in a thread-safe manner.

Practice

(1/5)
1. In designing the Player abstraction for the Snake and Ladder game, should Player be an abstract class or an interface, and why?
easy
A. Player should be an interface because it only defines behavior without state.
B. Player should be an abstract class because it holds common state like current position and provides default methods.
C. Player should be an interface to allow multiple inheritance of behaviors.
D. Player should be an abstract class to enforce implementation of all methods in subclasses.

Solution

  1. Step 1: Analyze state and behavior requirements

    The Player has common state such as current position on the board and methods like move().
  2. Step 2: Choose abstraction type

    An abstract class allows sharing state and default method implementations, which interfaces (pre-Java 8) cannot provide.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Abstract class holds shared state and behavior [check]
Hint: State + default behavior -> abstract class, not interface [check]
Common Mistakes:
2. The auction system currently calculates fees with a fixed strategy. To add a WeekendFeeStrategy without modifying existing classes, which SOLID principle is being applied and what is the minimum required change?
medium
A. Open/Closed Principle: create a new WeekendFeeStrategy class implementing FeeStrategy; inject it without changing existing code.
B. Liskov Substitution Principle: subclass Auction to WeekendAuction overriding fee calculation.
C. Single Responsibility Principle: modify Auction class to handle weekend fees directly.
D. Dependency Inversion Principle: hardcode weekend fee logic inside FeeStrategy implementation.

Solution

  1. Step 1: Identify SOLID principle

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: Apply OCP by adding WeekendFeeStrategy implementing FeeStrategy interface without modifying existing classes.

  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class + injection -> OCP compliance [check]
Hint: Add new strategy class, no existing code change -> OCP [check]
Common Mistakes:
3. To add a new WeekendFeeStrategy for orders without modifying existing classes, which SOLID principle is best applied and what is the minimal required change?
medium
A. Dependency Inversion Principle: inject WeekendFeeStrategy into Order by changing Order's constructor.
B. Open/Closed Principle: create a new WeekendFeeStrategy class implementing FeeStrategy interface, no changes to Order.
C. Liskov Substitution Principle: subclass Order to WeekendOrder with weekend fee logic.
D. Single Responsibility Principle: modify Order class to handle weekend fees directly.

Solution

  1. Step 1: Identify SOLID principle for extension without modification

    Open/Closed Principle (OCP) states classes should be open for extension but closed for modification.
  2. Step 2: Apply Dependency Inversion Principle (DIP) for injecting new strategy

    Injecting WeekendFeeStrategy via constructor or setter respects DIP and allows Order to use new fee logic without internal changes.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Inject new strategy -> no Order modification -> DIP + OCP [check]
Hint: Inject new strategy -> respect DIP and OCP [check]
Common Mistakes:
4. In the Stack Overflow voting system, if the castVote() method is not synchronized, which exact sequence of thread interleaving causes a double vote count for the same user on a question?
medium
A. Thread 1 reads vote count, Thread 2 reads vote count, both increment and write back simultaneously
B. Thread 1 checks if user has voted -> false, context switch; Thread 2 checks if user has voted -> false; both proceed to cast vote
C. Thread 1 updates vote count, Thread 2 waits; Thread 1 finishes, Thread 2 updates vote count again
D. Thread 1 and Thread 2 both block on the same lock, causing deadlock

Solution

  1. Step 1: Identify the check-then-act race condition

    The critical issue is the check if the user has already voted. Without synchronization, both threads see no vote recorded and proceed.
  2. Step 2: Trace exact interleaving causing double vote

    Thread 1 checks voted -> false, context switch; Thread 2 checks voted -> false; both cast votes, causing double count.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-then-act gap without sync -> double vote [check]
Hint: Check-then-act gap -> double vote without sync [check]
Common Mistakes:
5. ElectricCar vehicles require ChargingSpots in the parking module. What is the minimum change to the current Java design to support this without modifying existing classes?
hard
A. Create a new ElectricCar class and a ChargingSpot class, then modify ParkingLot to handle both types explicitly.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP.
C. Modify Vehicle class to include a boolean isElectric field and add logic in existing parking methods.
D. Add a new interface Chargeable and make ElectricCar implement it, modifying existing Vehicle and Spot classes.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Minimal change means no modification to existing classes; adding new enums or modifying compatibility map violates this.
  2. Step 2: Use composition or flags to extend behavior

    Adding an isElectric boolean field to Vehicle and handling charging logic externally avoids class explosion and preserves OCP.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Flag in Vehicle + external logic -> minimal change, no class modification [check]
Hint: Add flag + external logic -> minimal change, no class edits [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