As software systems get bigger and more complex, it becomes very important to write clean and easy-to-maintain code. Developers use a set of rules called SOLID principles to help make software easier to understand, test, maintain, and improve. These principles were created by Robert C. Martin (Uncle Bob) and are commonly used in object-oriented programming languages like Java, C#, and C++.
What is SOLID ?
SOLID =
S → Single Responsibility Principle (SRP)
O → Open/Closed Principle (OCP)
L → Liskov Substitution Principle (LSP)
I → Interface Segregation Principle (ISP)
Get Dharaka Deshan’s stories in your inbox
Join Medium for free to get updates from this writer.
D → Dependency Inversion Principle (DIP)
1. Single Responsibility Principle
The Single Responsibility Principle states that a class should have only one responsibility or one reason to change.
A class should perform only one specific task. If a class handles multiple responsibilities, it becomes difficult to maintain and modify. Separating responsibilities makes the code cleaner and easier to understand.
Example —
Instead of creating one
Studentclass that stores student information, saves data, prints reports, and sends emails, these tasks should be divided into separate classes.
Bad Example
//Here one class is doing many jobs (not recommended):
class Student {
String name;
void saveToDatabase() {
System.out.println("Saving student...");
}
void sendEmail() {
System.out.println("Sending email...");
}
void printReport() {
System.out.println("Printing report...");
}
}
//Problem:
//One class handles database, email, and report
//Hard to maintain and changeGood Example
// Each class has only one responsibility
class Student {
String name;
}
class StudentRepository {
void save(Student student) {
System.out.println("Saving student...");
}
}
class EmailService {
void sendEmail(Student student) {
System.out.println("Sending email...");
}
}
class ReportPrinter {
void print(Student student) {
System.out.println("Printing report...");
}
}2. Open/Closed Principle
The Open/Closed Principle states that software entities should be open for extension but closed for modification.
Instead of changing existing code whenever a new feature is added, developers should extend the application by creating new classes.
Bad Example
class Payment {
public void pay(String method){
if(method.equals("Cash")){
System.out.println("Cash Payment");
}
else if(method.equals("Card")){
System.out.println("Card Payment");
}
}
}
// imagine Now customer wants add
// paypal , apple pay
// then must keep changin this class and its Not Good.Good Example
// Create an interface
interface Payment {
void pay();
}
// Cash payment
class CashPayment implements Payment {
public void pay(){
System.out.println("Cash Payment");
}
}
//Card Payment
class CardPayment implements Payment {
public void pay(){
System.out.println("Card Payment");
}
}
// in here Old code never changes. Only new classes are added.3. Liskov Substitution Principle
The Liskov Substitution Principle states that objects of a subclass should be replaceable with objects of the parent class without affecting program correctness.
A child class should behave correctly wherever its parent class is expected.
in simple : Anywhere you use a parent object, you should also be able to use its child object
Bad Example
class Bird{
public void fly(){
System.out.println("Flying");
}
public void walk(){
System.out.println("walking");
}
}
class Penguin extends Bird{
@Override
public void walk(){
System.out.println("Penguins can walk");
}
@Override
public void fly(){
throw new UnsupportedOperationException();
}
}
// Penguins cannot fly.
// So Penguin should NOT inherit Bird if Bird promises flying.
// This breaks Liskov Substitution Principle.Good Example
class Bird {
puublic void walk(){
System.out.println("walking");
}
}
// Only birds that can fly extend FlyingBird
class FlyingBird extends Bird {
public void fly() {
System.out.println("Flying");
}
}
// Sparrow can fly
class Sparrow extends FlyingBird {
@Override
public void fly() {
System.out.println("Sparrow is flying");
}
}
// Penguin cannot fly, so it extends Bird only
class Penguin extends Bird {
@Override
public void walk() {
System.out.println("Penguin is walking");
}
}4. Interface Segregation Principle
The Interface Segregation Principle states that a class should not be forced to implement methods it does not use.
Large interfaces should be divided into smaller, more specific interfaces.
Bad Example
interface Worker{
void work();
void eat();
void sleep();
}
class Robot implements Worker{
public void work(){
System.out.println("Working");
}
public void eat(){
}
public void sleep(){
}
}
// in here Robot doesn't eat or sleep.Good Example
// Split interfaces
interface Workable{
void work();
}
interface Eatable{
void eat();
}
// Human
class Human implements Workable,Eatable{
public void work(){
System.out.println("Working");
}
public void eat(){
System.out.println("Eating");
}
}
// Robot
class Robot implements Workable{
public void work(){
System.out.println("Working");
}
}
// The Robot class only implements the methods it actually needs.
// Now code is Much cleaner.5. Dependency Inversion Principle
The Dependency Inversion Principle states that high-level modules should depend on abstractions rather than concrete implementations.(High-level classes should not depend directly on low-level classes)
Instead of directly using a specific class, programs should depend on interfaces. This makes it easier to replace implementations without changing business logic.
Bad Example
// DB
class MySQLDatabase{
public void connect(){
System.out.println("Connected to MySQL");
}
}
// StudentService file
class StudentService{
MySQLDatabase db = new MySQLDatabase();
public void save(){
db.connect();
}
}
// Problem
// Later you change to PostgreSQL.
// You must modify StudentService.Good Example
//Create interface
interface Database{
void connect();
}
//PostgreSQL
class PostgreSQLDatabase implements Database{
public void connect(){
System.out.println("Connected PostgreSQL");
}
}
// Student Service file
class StudentService{
private Database database;
public StudentService(Database database){
this.database = database;
}
public void save(){
database.connect();
}
}
// .No changes to StudentService.Advantages of SOLID Principles
Applying SOLID principles provides several advantages
- Improves code readability.
- Makes software easier to maintain.
- Reduces code duplication.
- Simplifies testing.
- Supports future enhancements.
- Encourages reusable components.
- Produces flexible and scalable applications.







