SOLID is an acronym representing a set of five fundamental principles of object-oriented programming and design. The goal of these principles is to create software that is more understandable, flexible, and maintainable
simple explanation
Think of building with Lego blocks:
-
SRP: Each Lego block has a single, clear purpose (a 2x4 brick, a wheel, a window).
-
OCP: You can combine these blocks in endless new ways (extend) without having to melt down and re-mold existing blocks (modify).
-
LSP: A 2x2 brick can fit anywhere a 2x2 space is designed. It’s perfectly substitutable.
-
ISP: You have specialized pieces for different jobs, rather than one giant “do-everything” piece that forces you to have parts you don’t need.
-
DIP: The design of your Lego castle (the high-level plan) doesn’t depend on the specific color of the bricks (the low-level detail). You can swap red bricks for blue ones without changing the design.
S - Single Responsibility Principle (SRP)
“A class should have one, and only one, reason to change.” A class should have only one job or responsibility. It should not be overwhelmed with multiple tasks.
-
Bad Example: A
Bookclass that handles its own data (title, author) and saves itself to a database and prints its own format to the console. -
Good Example:
-
Bookclass: Only holds data and business logic related to a book (title, author, getSummary()). -
BookRepositoryclass: Handles all database operations (save, delete, find). -
BookPrinterclass: Handles all formatting and printing of book details.
-
O - Open/Closed Principle (OCP)
“Software entities (classes, modules, functions) should be open for extension, but closed for modification.” You should be able to add new functionality without changing existing, working code. You extend behavior, not modify it.
-
Bad Example: A
AreaCalculatorclass with a single method that uses a giantif-elseorswitchstatement to check the type of shape (circle, square) to calculate its area. To add a triangle, you must modify this method. -
Good Example: Define a
Shapeinterface with a methodcalculateArea(). Then, haveCircle,Square, and laterTriangleclasses implement this interface. TheAreaCalculatorcan now work with anyShapewithout knowing its concrete type. To add a new shape, you create a new class without touching the calculator.
L - Liskov Substitution Principle (LSP)
“Subtypes must be substitutable for their base types.”
If class B is a subclass of class A, then you should be able to pass an object of B to any program that expects an object of A without causing errors or unexpected behavior.
-
Bad Example: A
Squareclass inheriting from aRectangleclass. ARectanglehas independentwidthandheight. ASquarehas equal sides. If you override the setters inSquareto force width and height to be equal, a function that expects aRectangle(e.g., one that doubles its width) will break when given aSquare. -
Good Example: Both
RectangleandSquarecould implement aShapeinterface or inherit from a more generalQuadrilateralclass that doesn’t make assumptions about side equality.
I - Interface Segregation Principle (ISP)
“Clients should not be forced to depend on interfaces they do not use.” Instead of one large, “fat” interface, create multiple, smaller, and more specific interfaces.
-
Bad Example: A monolithic
Workerinterface with methodswork(),eat(), andsleep(). ARobotclass implementing this would be forced to implementeat()andsleep(), which make no sense. -
Good Example:
-
Workableinterface:work() -
Feedableinterface:eat() -
A
Humanclass can implement all three. ARobotclass only needs to implementWorkable.
-
D - Dependency Inversion Principle (DIP)
A. High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces).
B. Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
This principle is about decoupling. High-level policy (business logic) should not be tightly coupled to low-level implementation details (like databases, APIs, or logging frameworks).
-
Bad Example: A
ReportServiceclass directly creates an instance of aMySQLDatabaseclass to save data. If you want to switch to aPostgreSQLDatabase, you have to change the code insideReportService. -
Good Example: The
ReportServicedepends on aDatabaseinterface (abstraction). TheMySQLDatabaseandPostgreSQLDatabaseclasses are the details that implement this interface. TheReportServiceis injected with a concrete (not abstract) implementation (e.g., via a constructor), a technique known as Dependency Injection.
dependency injection
instead of an object creating its own dependencies, they are provided to it from the outside.