Design: SOLID Principles Violation and Fix Demonstration
In scope: Identifying and fixing violations of Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion principles. Out of scope: Full application implementation or complex business logic.
Functional Requirements
FR1: Show examples of common SOLID principle violations in a simple system
FR2: Explain how to fix each violation with proper design changes
FR3: Demonstrate the impact of fixes on code maintainability and scalability
Non-Functional Requirements
NFR1: Use simple, understandable examples suitable for beginners
NFR2: Focus on design and architecture, not language-specific syntax
NFR3: Keep examples small and focused on one principle violation at a time
Think Before You Design
Questions to Ask
❓ Question 1
❓ Question 2
❓ Question 3
❓ Question 4
❓ Question 5
Key Components
Classes or modules violating a principle
Interfaces or abstractions to separate concerns
Dependency injection mechanisms
Extension points for open/closed principle
Design Patterns
Single Responsibility Principle (SRP)
Open/Closed Principle (OCP)
Liskov Substitution Principle (LSP)
Interface Segregation Principle (ISP)
Dependency Inversion Principle (DIP)
Reference Architecture
+----------------+ +-------------------+
| Client Code | ----> | Service Interface |
+----------------+ +-------------------+
^
|
+-------------------------------+
| Concrete Service |
+-------------------------------+
Violations occur when:
- Client depends on concrete classes directly
- Classes have multiple responsibilities
- Interfaces force methods not needed by clients
- Subclasses break expected behavior
Fixes:
- Separate responsibilities into classes
- Use interfaces for abstraction
- Follow LSP by ensuring subclasses behave correctly
- Split interfaces into smaller ones
- Inject dependencies via abstractions
Components
Client Code
Any OO language
Uses services via interfaces, should not depend on concrete implementations
Service Interface
Interface or abstract class
Defines contract for services, enabling open/closed and dependency inversion
Concrete Service
Class implementing interface
Implements service logic, follows single responsibility
Request Flow
1. Client calls methods on Service Interface, not concrete classes
2. Concrete Service implements interface methods with focused responsibility
3. Dependencies are injected into Client or Service to avoid tight coupling
4. Interfaces are split so clients only depend on needed methods
5. Subclasses override behavior without breaking expected contracts
Database Schema
Not applicable - focus is on class design and interfaces
Scaling Discussion
Bottlenecks
Tight coupling makes changes risky and slow
Large classes with multiple responsibilities are hard to maintain
Clients forced to depend on unused methods cause unnecessary changes
Subclasses that break base class behavior cause bugs
Hard-coded dependencies reduce flexibility
Solutions
Apply Single Responsibility Principle to split classes
Use Open/Closed Principle to extend behavior without modifying existing code
Follow Interface Segregation Principle to create focused interfaces
Ensure Liskov Substitution Principle by designing correct inheritance
Use Dependency Inversion Principle to depend on abstractions and inject dependencies
Interview Tips
Time: Spend 10 minutes explaining each SOLID principle violation with example, then 5 minutes on fixes and benefits.
Explain what each SOLID principle means in simple terms
Show clear examples of violations and why they cause problems
Describe how fixes improve code quality and system flexibility
Mention how these principles help scale and maintain large systems
Use analogies like separating chores among people to explain SRP
Practice
(1/5)
1. Which SOLID principle is violated if a class has too many responsibilities, making it hard to maintain?
easy
A. Single Responsibility Principle
B. Open/Closed Principle
C. Liskov Substitution Principle
D. Interface Segregation Principle
Solution
Step 1: Understand the problem of multiple responsibilities
A class with many responsibilities is hard to maintain and change because changes in one responsibility may affect others.
Step 2: Identify the violated SOLID principle
The Single Responsibility Principle states that a class should have only one reason to change, meaning one responsibility.
Final Answer:
Single Responsibility Principle -> Option A
Quick Check:
One class, one responsibility = C [OK]
Hint: One class, one job means Single Responsibility Principle [OK]
Common Mistakes:
Confusing Single Responsibility with Open/Closed Principle
Thinking Liskov Substitution relates to responsibilities
Mixing Interface Segregation with class responsibilities
2. Which of the following code snippets correctly applies the Open/Closed Principle by allowing extension without modification? class Shape { draw() { /* draw shape */ } } class Circle extends Shape { draw() { /* draw circle */ } }
easy
A. Adding new shape classes by extending Shape without changing existing code
B. Modifying the Shape class draw method to handle all shapes
C. Using a single class for all shapes with if-else inside draw
D. Changing Circle class to override Shape's draw method with new logic
Solution
Step 1: Understand Open/Closed Principle
This principle says software entities should be open for extension but closed for modification.
Step 2: Analyze the code snippet
Extending Shape with new classes like Circle adds new behavior without changing existing Shape code.
Final Answer:
Adding new shape classes by extending Shape without changing existing code -> Option A
Overriding methods incorrectly causing side effects
3. Given the following code violating Interface Segregation Principle, what is the output when calling robot.work()?
interface Worker { void work(); void eat(); } class Robot implements Worker { public void work() { System.out.println("Robot working"); } public void eat() { throw new UnsupportedOperationException(); } } Robot robot = new Robot(); robot.work();
medium
A. No output
B. UnsupportedOperationException thrown
C. Compilation error
D. Robot working
Solution
Step 1: Understand the code behavior
The robot class implements work() by printing "Robot working" and eat() throws an exception.
Step 2: Analyze the method called
Only robot.work() is called, which prints the message without error.
Final Answer:
Robot working -> Option D
Quick Check:
Calling work() prints message = A [OK]
Hint: Calling only implemented methods avoids exceptions [OK]
Common Mistakes:
Assuming eat() is called causing exception
Thinking interface violation causes compile error
Ignoring method actually called in code
4. Identify the main problem in this code violating Liskov Substitution Principle:
class Bird { void fly() { System.out.println("Flying"); } } class Ostrich extends Bird { void fly() { throw new UnsupportedOperationException(); } }
medium
A. Bird class should not have fly method
B. Ostrich should override fly with different signature
C. Ostrich cannot replace Bird without breaking behavior
D. No violation, code is correct
Solution
Step 1: Understand Liskov Substitution Principle
Subtypes must be replaceable for their base types without altering correctness.
Step 2: Analyze Ostrich behavior
Ostrich throws exception on fly(), breaking expected behavior of Bird.
Final Answer:
Ostrich cannot replace Bird without breaking behavior -> Option C
Quick Check:
Subtype must behave like base = B [OK]
Hint: Subtype must not break base class behavior [OK]
Common Mistakes:
Thinking method signature change fixes violation
Ignoring exception breaking substitution
Assuming base class design is wrong here
5. You have a payment system where the PaymentProcessor interface has methods payWithCreditCard(), payWithPayPal(), and payWithCrypto(). A new payment method is added, but many classes implementing this interface do not support all methods, causing empty or error implementations. How should you fix this design following SOLID principles?
hard
A. Remove unsupported methods from implementations silently
B. Split the interface into smaller ones for each payment method
C. Keep one interface and add flags to check supported methods
D. Add default empty implementations to the interface methods
Solution
Step 1: Identify the violated principle
Having one interface with many methods causes classes to implement unsupported methods, violating Interface Segregation Principle.
Step 2: Apply the fix
Splitting the interface into smaller, specific interfaces lets classes implement only what they support, improving design.
Final Answer:
Split the interface into smaller ones for each payment method -> Option B
Quick Check:
Small interfaces, no unused methods = D [OK]
Hint: Use many small interfaces, not one big one [OK]