DEV Community

Sunil Mopuru
Sunil Mopuru

Posted on

Solid Principles

The SOLID principles are a set of five object-oriented design principles that help developers create more maintainable, understandable, and flexible software systems. They are often applied during software development using object-oriented programming languages like Java.

Acronym Principle Description
S Single Responsibility Principle (SRP) A class should have only one reason to change.
O Open/Closed Principle (OCP) A class should be open for extension but closed for modification.
L Liskov Substitution Principle (LSP) Subtypes must be substitutable for their base types.
I Interface Segregation Principle (ISP) No client should be forced to depend on methods it does not use.
D Dependency Inversion Principle (DIP) High-level modules should not depend on low-level modules. Both should depend on abstractions.

Java Case Study Example: Online Notification System
We will design a notification system that supports Email and SMS notifications. Later, we’ll apply each SOLID principle to improve the system.

❌ Initial (Non-SOLID) Design:
java

public class NotificationService {
    public void sendNotification(String type, String message) {
        if (type.equals("EMAIL")) {
            System.out.println("Sending Email: " + message);
        } else if (type.equals("SMS")) {
            System.out.println("Sending SMS: " + message);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Applying SOLID Principles:

  1. Single Responsibility Principle (SRP) Problem: NotificationService handles both logic and message formatting.

Fix: Split responsibilities into separate classes.

public class EmailSender {
    public void send(String message) {
        System.out.println("Sending Email: " + message);
    }
}

public class SmsSender {
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, each class has one reason to change: either Email or SMS functionality.

2. Open/Closed Principle (OCP)

Problem: To add a new notification type (e.g., Push), we must modify NotificationService.
Fix: Use abstraction and polymorphism to allow extensions.

public interface Notification {
    void send(String message);
}

public class EmailNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending Email: " + message);
    }
}

public class SmsNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

public class NotificationService {
    private Notification notification;

    public NotificationService(Notification notification) {
        this.notification = notification;
    }

    public void send(String message) {
        notification.send(message);
    }
}

Enter fullscreen mode Exit fullscreen mode

Now we can add PushNotification without modifying existing code.

3. Liskov Substitution Principle (LSP)

Problem: If a subclass overrides a method in a way that breaks functionality, LSP is violated.
Fix: Ensure all subclasses can be used interchangeably.

public class PushNotification implements Notification {
    public void send(String message) {
        System.out.println("Sending Push Notification: " + message);
    }
}

// Works with existing NotificationService
Notification notification = new PushNotification();
notification.send("LSP test");

Enter fullscreen mode Exit fullscreen mode

PushNotification behaves like other Notification implementations — no behavior breaks.

4. Interface Segregation Principle (ISP)

Problem: A fat interface might force classes to implement unused methods.
Fix: Split interfaces based on specific roles.

public interface EmailSender {
    void sendEmail(String message);
}

public interface SmsSender {
    void sendSms(String message);
}

// Implement only what you need
public class EmailService implements EmailSender {
    public void sendEmail(String message) {
        System.out.println("Sending Email: " + message);
    }
}

Enter fullscreen mode Exit fullscreen mode

5. Dependency Inversion Principle (DIP)

Problem: High-level modules depend on low-level modules.
Fix: Depend on abstractions (interfaces), not concrete classes.

public class NotificationManager {
    private final Notification notification;

    public NotificationManager(Notification notification) {
        this.notification = notification;
    }

    public void notifyUser(String message) {
        notification.send(message);
    }
}

Enter fullscreen mode Exit fullscreen mode

Now NotificationManager depends on the abstraction Notification, not on EmailNotification or SmsNotification.

✅ Final Summary

Principle Applied Example
SRP Split Email/SMS into separate classes
OCP New types added without modifying base code
LSP All notifications behave similarly
ISP Interfaces are small and focused
DIP NotificationManager depends on Notification interface

Top comments (0)