๐Ÿงฑ Full Lesson ยท Programming Concepts
Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion
SOLID Principles

Five design guidelines that, followed together, produce object-oriented code that's easier to extend, test, and maintain โ€” and that, violated, produce the tangled, fragile class hierarchies every experienced developer has learned to dread.

The Core Idea
Five Guidelines for Maintainable Object-Oriented Design

SOLID is an acronym for five object-oriented design principles, each addressing a different way that classes and their relationships can become fragile, difficult to extend, or hard to test if designed carelessly. These principles build directly on the 4 Pillars of OOP โ€” they're guidelines for HOW to apply encapsulation, abstraction, inheritance, and polymorphism well, rather than entirely separate concepts.

Violating SOLID principles doesn't necessarily produce code that's immediately, obviously broken โ€” it typically produces code that WORKS today but becomes increasingly painful to extend, test, or safely modify as the system grows and requirements change over time, which is exactly why these principles matter more for long-term software health than for getting a first version working.

๐Ÿ’ก Memory Trick
SINGLE RESPONSIBILITY: a Swiss Army knife tool that ONLY opens bottles does exactly one job well. OPEN/CLOSED: a power strip lets you plug in NEW devices without needing to rewire the strip itself. LISKOV SUBSTITUTION: any brand of AA battery should work correctly in a device designed for AA batteries, with no surprises. INTERFACE SEGREGATION: a restaurant menu split into separate breakfast, lunch, and dinner menus, rather than forcing every customer to flip through one giant combined menu listing options they'll never order. DEPENDENCY INVERSION: a lamp plugs into a standard wall outlet interface, not directly wired to one specific power plant โ€” the lamp depends on the OUTLET STANDARD, not on the specific power source behind it.
The Five Principles
What Each One Specifically Guards Against
S
Single Responsibility Principle
A class should have only one reason to change โ€” meaning it should be responsible for exactly one specific piece of functionality, not several unrelated ones bundled together. Violating this means a single class handles multiple unrelated concerns (like a class that both processes payment AND sends confirmation emails), so a change to one concern risks breaking the other.
O
Open/Closed Principle
Classes should be open for EXTENSION but closed for MODIFICATION โ€” you should be able to add new behavior by adding new code (like a new subclass), without needing to modify existing, already-tested code. Violating this typically means adding a new feature requires directly editing an existing class's internals, risking breaking its existing, previously-working behavior.
L
Liskov Substitution Principle
Any object of a subclass should be usable anywhere an object of its parent class is expected, without breaking the correctness of the program. Violating this means a subclass technically inherits from a parent class but behaves in a way that breaks the parent's expected contract โ€” the classic example is a Square class inheriting from Rectangle but overriding setWidth() to also change height (to preserve being a square), which breaks code that correctly expected a Rectangle's width and height to be independently settable.
I
Interface Segregation Principle
Clients shouldn't be forced to depend on interface methods they don't actually use. Violating this means a single large interface bundles many unrelated methods together, forcing every implementing class to provide (often meaningless or empty) implementations for methods that don't actually apply to it.
D
Dependency Inversion Principle
High-level code should depend on abstractions (interfaces), not directly on specific low-level implementation details. Violating this means high-level business logic is tightly coupled directly to a specific concrete implementation (like a specific database library), making it hard to swap in a different implementation later or to test the business logic in isolation without that specific dependency.
Why These Guidelines Matter Together
Reducing the Ripple Effect of Change

The unifying theme across all five principles is minimizing the RIPPLE EFFECT of change โ€” a well-designed system following SOLID lets you add new features, fix bugs, or swap implementations while touching as little existing, already-working code as possible, since each principle specifically closes off a different way that a seemingly-small change could unexpectedly break unrelated parts of the system.

These principles are guidelines, not absolute laws to apply mechanically everywhere โ€” over-applying SOLID (creating excessive abstraction layers and interfaces for genuinely simple problems that don't need that flexibility) can itself create unnecessary complexity; experienced developers apply these principles where the actual risk of future change and the cost of tight coupling genuinely justify the added structure, not reflexively on every single class regardless of context.

๐Ÿ–ฅ๏ธ Applied Scenario
A ReportGenerator class currently both calculates report data AND formats it directly into a PDF file, and a new requirement arrives to also support exporting reports as Excel spreadsheets.
1
You recognize the current design violates Single Responsibility, since ReportGenerator handles both data calculation AND a specific output format, bundling two genuinely separate concerns into one class.
2
You separate these concerns: ReportGenerator now handles only calculating report data, while a new, separate PdfExporter class (and later, ExcelExporter) handles taking that calculated data and formatting it into a specific output format.
3
Following the Open/Closed Principle, adding Excel support now means adding a brand-new ExcelExporter class implementing a shared Exporter interface, without needing to modify the already-working, already-tested ReportGenerator or PdfExporter classes at all.
4
Conclusion: because the design correctly separated responsibilities and remained open to extension, adding an entirely new export format required only NEW code, with zero risk of accidentally breaking the existing, previously-working PDF export functionality.
๐Ÿ“Œ Exam Application
Exam questions frequently present a code example violating one specific SOLID principle and ask you to identify which principle is being violated and explain why, or to propose a refactoring that correctly fixes the violation. You may also be asked to define all five principles from memory and explain, in one sentence each, what specific problem each one guards against.
โš ๏ธ Most Common SOLID Principles Mistakes
The most common mistake is confusing which specific principle a described violation demonstrates โ€” particularly Single Responsibility (one class doing too many unrelated things) versus Interface Segregation (one interface forcing implementers to support too many unrelated methods), since both involve 'too much bundled together,' but at different levels (a class's responsibilities vs. an interface's method contract). Another frequent error is applying SOLID mechanically to every class regardless of actual need โ€” over-engineering a genuinely simple, unlikely-to-change piece of code with excessive interfaces and abstraction layers to satisfy SOLID 'by the book' can introduce unnecessary complexity without any corresponding real-world benefit.
โœ“ Quick Self-Test
Can you name and define all five SOLID principles from memory, in your own words? Given a small code example violating one specific principle, can you correctly identify which one it violates and propose a specific refactoring that resolves it?
Next Lesson
Functional Programming
โ†’
โ† All Programming Concepts Lessons