Hello! Welcome to the next lesson in our journey through Low-Level Design.
In the previous module, we dedicated a lesson to each of the five SOLID principles, understanding their individual purpose and how to apply them. We concluded with the Dependency Inversion Principle, which tied everything together by showing how to decouple high-level business logic from low-level implementation details.
Today's lesson builds directly on that foundation. We will focus on the learning outcome: Identify SOLID principle violations in existing code examples. Instead of learning new principles, our goal is to synthesize what we've learned and train our eyes to spot the "code smells" that indicate when these principles are being broken. This is a critical skill for code reviews, maintaining a healthy codebase, and, importantly, for system design interviews where you might be asked to critique a given design.
1. A Guide to Spotting SOLID Violations
Think of the SOLID principles as a guide to writing clean, maintainable object-oriented code. When code deviates from these principles, it often develops "smells"—symptoms of deeper design problems. Learning to recognize these smells is the first step toward fixing them.
We'll go through each principle, highlighting the common anti-patterns and code smells that signal a violation. The following article provides excellent "before" and "after" examples for each principle, and also has a great summary of common anti-patterns. We will use it as a reference throughout this section.
SOLID Principles in Java: A Practical Guide
The article 'SOLID Principles in Java: A Practical Guide' from Medium is a fantastic resource for this lesson. It clearly illustrates violations and provides refactored solutions. We will focus on the 'Before' examples to practice our identification skills.
I'll be guiding you through specific sections of this article as we discuss each principle. For now, just have it open and ready. We'll start by looking at the anti-patterns described at the end of the article, as they provide a great overview.
Let's begin by examining the tell-tale signs for each principle.
Single Responsibility Principle (SRP) Violation
- Reminder: A class should have only one reason to change.
- Code Smell: The "God Class"
- The class has a name that implies multiple jobs (e.g.,
UserManagerAndReporter). - You find yourself describing what the class does using the word "and" multiple times.
- The class contains methods that handle completely different concerns, such as business logic, database persistence, and data formatting, all mixed together.
- A small change in one feature (e.g., changing an email format) requires you to modify and re-test a large, complex class that also handles user authentication and database logic.
- The class has a name that implies multiple jobs (e.g.,
Example Violation:
The UserManager class in the "Common Anti-patterns" section of the SOLID Principles in Java article is a perfect example of a God Class. It mixes user registration, email sending, activity logging, and report generation. Each of these is a separate responsibility.
Similarly, consider the Video class in the video below. It initially handles both data representation and database persistence, a clear violation of SRP.
SOLID Principles in Java Explained | Object-Oriented Clean Code & Design | Geekific Remastered
This video from Geekific shows a clear SRP violation. Notice how the initial Video class is responsible for both its own data and for persisting itself to a database.
Watch from 00:52 to 02:10. Focus on identifying the two distinct responsibilities that are incorrectly combined in the Video class.
Open/Closed Principle (OCP) Violation
- Reminder: Software entities should be open for extension but closed for modification.
- Code Smell: The
if/else iforswitchChain- You see a block of code that uses
instanceofor checks an enum/type field to decide which logic to execute. - Adding a new type (e.g., a new kind of report, a new shape, a new event type) requires you to go back and add another
else iforcaseto an existing class. This means the class is open for modification, which violates the principle.
- You see a block of code that uses
Example Violation:
The image below is a classic example of an OCP violation. To handle a new event type, a developer would have to modify the handleEvent method directly.

The "Before OCP" example in the SOLID Principles in Java article, with the AreaCalculator that uses instanceof to check for Rectangle or Circle, is another perfect illustration of this anti-pattern.
Liskov Substitution Principle (LSP) Violation
- Reminder: Subclasses should be substitutable for their base classes without altering the program's correctness.
- Code Smell: The "Surprising" Subclass
- A method in a subclass is overridden to do nothing, or worse, to throw an
UnsupportedOperationException. - A subclass changes a fundamental behavior or invariant of the parent class. The classic example is the Square/Rectangle problem. A
Square"is-a"Rectangle, but if you set its width, its height must also change, which is not true for aRectangle. This change in behavior can break client code that expects aRectangle.
- A method in a subclass is overridden to do nothing, or worse, to throw an
Example Violation:
The SOLID Principles in Java article provides a great explanation of the Square-Rectangle problem. Read the "Before" code example in the LSP section (section 2) to see how the Square subclass behaves in a way that a client using a Rectangle reference would not expect, leading to incorrect calculations.
Another clear violation is shown in the video below, where a NoShape class implements the Shape interface but throws an exception, making it non-substitutable.
Learn SOLID Principles with CLEAN CODE Examples
This clip from the Amigoscode video demonstrates a clear LSP violation. A subclass that cannot fulfill the contract of its parent is not truly substitutable.
Watch from 15:47 to 17:35. Pay attention to why the NoShape class, despite implementing the Shape interface, breaks the program when used as a Shape.
Interface Segregation Principle (ISP) Violation
- Reminder: Clients should not be forced to depend on methods they do not use.
- Code Smell: The "Fat Interface"
- An interface contains many methods that cover different, potentially unrelated, functionalities.
- Classes implementing the interface are forced to provide empty implementations or throw exceptions for methods they don't need or can't support.
- If you see
UnsupportedOperationExceptionin an overridden method, it's a strong hint of either an ISP or LSP violation.
Example Violation:
The "Before ISP" example in the SOLID Principles in Java article is canonical. The Machine interface includes print, scan, fax, and copy. A SimplePrinter class is forced to implement all four methods, even though it can only print. This is a "fat interface" that needs to be segregated.
Dependency Inversion Principle (DIP) Violation
- Reminder: Depend on abstractions, not concretions.
- Code Smell: Tight Coupling to Implementation Details
- You see the
newkeyword used to create an instance of a low-level utility or service class within a high-level business logic class (e.g.,private EmailSender emailSender = new EmailSender();). - A high-level class directly imports and references a concrete low-level class (e.g.,
MySQLDatabase) instead of an interface (DatabaseRepository). - Changing a low-level implementation detail (e.g., swapping a MySQL database for a PostgreSQL database) requires you to change the code in your high-level business modules.
- You see the
Example Violation:
Your experience with Spring Boot and dependency injection gives you a great advantage here. You know that injecting interfaces (@Autowired private MessageSender sender;) is the norm. Any code that manually instantiates a concrete dependency inside a class, like the "Without DIP" example in the SOLID Principles in Java article (section 4), is a clear violation. This creates the tight coupling that frameworks like Spring are designed to prevent.
Test your understanding!
You are reviewing a colleague's code for a simple e-commerce application. You find the following Product class:
public class Product {
private String name;
private double price;
// constructor, getters, setters
public String getProductAsHtml() {
return "<div><h1>" + name + "</h1><p>" + price + "</p></div>";
}
public void saveToDatabase(DatabaseConnection conn) {
// logic to save product to a SQL database
}
public void applyDiscount(String userRole) {
if ("premium".equals(userRole)) {
this.price *= 0.9;
} else if ("gold".equals(userRole)) {
this.price *= 0.95;
}
// no discount for regular users
}
}
Which SOLID principles does this class violate, and why? Think about at least two violations.
Show answer
This class has several violations:
-
Single Responsibility Principle (SRP): The
Productclass has at least three responsibilities:- Holding product data (its primary job).
- Formatting the product as HTML (
getProductAsHtml). This is a presentation concern. - Saving the product to a database (
saveToDatabase). This is a persistence concern.
-
Open/Closed Principle (OCP): The
applyDiscountmethod violates OCP. If a new user role (e.g., "platinum") with a different discount is introduced, you would have to modify this method by adding anotherelse ifblock. -
Dependency Inversion Principle (DIP): The
saveToDatabasemethod likely violates DIP because it probably depends on a concreteDatabaseConnectionclass, coupling theProductentity directly to a specific database implementation.
2. A Practice Kata
Now it's time to practice spotting these issues in a repository designed for this purpose. The following GitHub repository contains small Java projects, each violating one of the SOLID principles.
Some exercises about solid principles and their solutions
This GitHub repository, 'solid-kata' by jahs-es, provides simple, focused exercises for each SOLID principle. We'll use it for a quick practice run.
Navigate to the repository. You will see folders named srp, ocp, lsp, isp, and dip. Each contains Java code that violates the corresponding principle.
Your Task:
- Navigate into the
ocpfolder in thesolid-katarepository. - Examine the
AreaCalculator.javafile. - Without fixing the code, articulate exactly how this class violates the Open/Closed Principle. What would you have to do to add a
Triangleshape, and why is that a problem?
Take a few minutes to analyze the code before checking the answer below.
Show your analysis
The AreaCalculator class violates OCP because its area method uses an if-else if block to check the type of each shape.
To add a Triangle shape, you would be forced to modify the AreaCalculator class by adding another else if (shape instanceof Triangle) block. This is a problem because the AreaCalculator should be closed for modification. A well-designed system would allow you to add the new Triangle class (extension) without touching the existing, tested AreaCalculator code.
Conclusion
Today, we've shifted from learning what the SOLID principles are to learning what code looks like when it breaks them. This ability to diagnose design problems is as important as knowing the solutions.
Key Takeaways:
- SRP Violation (God Class): A class does too many unrelated things (e.g., logic, persistence, presentation).
- OCP Violation (Modification Chains):
if/else iforswitchstatements that check object types indicate that the code is not closed for modification. - LSP Violation (Broken Substitutability): Subclasses that throw
UnsupportedOperationExceptionor alter the core behavior of the parent class are a red flag. - ISP Violation (Fat Interface): Forcing classes to implement methods they don't need creates brittle code.
- DIP Violation (Tight Coupling): Using
newon concrete low-level classes within high-level modules creates a rigid design that is hard to test and change.

Now that you can effectively identify these violations, you're ready for the next logical step. In our next lesson, we will focus on refactoring code to improve its adherence to SOLID principles, where we'll take the "before" examples and transform them into the clean "after" solutions.