Tuesday, 24 June 2025

SOLID principles in Java

 The SOLID principles are five design principles intended to make object-oriented software more understandable, flexible, and maintainable. These principles were popularized by Robert C. Martin (Uncle Bob) and are fundamental to clean code and good software architecture.


🧱 SOLID = S + O + L + I + D

PrincipleFull Form
SSingle Responsibility Principle (SRP)
OOpen/Closed Principle (OCP)
LLiskov Substitution Principle (LSP)
IInterface Segregation Principle (ISP)
DDependency Inversion Principle (DIP)

🔹 1. Single Responsibility Principle (SRP)

A class should have only one reason to change.

✅ What it means:

Each class should focus on a single task or responsibility. Don't mix multiple functionalities in one class.

✅ Java Example:

// ❌ Violates SRP: does too much class UserService { public void registerUser(User user) { /* logic */ } public void sendEmail(User user) { /* logic */ } } // ✅ SRP-compliant class UserService { public void registerUser(User user) { /* registration logic */ } } class EmailService { public void sendEmail(User user) { /* email logic */ } }

🔹 2. Open/Closed Principle (OCP)

Software entities (classes, modules, functions) should be open for extension, but closed for modification.

✅ What it means:

You should be able to add new functionality without modifying existing code.

✅ Java Example (Using Polymorphism):

interface Notification { void send(String message); } class EmailNotification implements Notification { public void send(String message) { System.out.println("Email: " + message); } } class SMSNotification implements Notification { public void send(String message) { System.out.println("SMS: " + message); } } // ✅ Client code uses abstraction public class NotificationService { public void notifyUser(Notification notification, String msg) { notification.send(msg); } }

🔹 3. Liskov Substitution Principle (LSP)

Subtypes must be substitutable for their base types without breaking functionality.

✅ What it means:

If class B is a subclass of class A, then A-typed objects can be replaced with B-typed objects without unexpected behavior.

LSP Violation Example (Simplified)

class Vehicle { public void startEngine() { System.out.println("Engine started"); } } class Bicycle extends Vehicle { @Override public void startEngine() { throw new UnsupportedOperationException("Bicycles don't have engines!"); } }

❌ Problem:

  • Bicycle is a subclass of Vehicle.

  • But calling startEngine() on a Bicycle object breaks the program — violating LSP.


Correct (LSP-compliant) Example


interface Vehicle { void move(); } interface MotorVehicle extends Vehicle { void startEngine(); } class Car implements MotorVehicle { public void startEngine() { System.out.println("Car engine started"); } public void move() { System.out.println("Car is moving"); } } class Bicycle implements Vehicle { public void move() { System.out.println("Bicycle is moving"); } }

✅ Explanation:

  • We separated startEngine() into MotorVehicle (not all vehicles need engines).

  • Car implements MotorVehicle, Bicycle implements only Vehicle.

  • Now, no object will fail unexpectedly, and everything remains substitutable via correct interfaces.


🔹 4. Interface Segregation Principle (ISP)

Clients should not be forced to depend on interfaces they do not use.

✅ What it means:

Break large interfaces into smaller, specific ones so that implementing classes only need to know about the methods that are relevant to them.

❌ Bad:

interface Worker { void work(); void eat(); } class Robot implements Worker { public void work() {} public void eat() {} // ❌ Doesn't make sense }

✅ Good:

interface Workable { void work(); } interface Eatable { void eat(); } class Human implements Workable, Eatable { public void work() {} public void eat() {} } class Robot implements Workable { public void work() {} }

🔹 5. Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions.

✅ What it means:

Use interfaces or abstract classes so that high-level classes are not tightly coupled to low-level class implementations.

❌ Violation:

class Keyboard {} class Monitor {} class Computer { private Keyboard keyboard = new Keyboard(); // tightly coupled private Monitor monitor = new Monitor(); }

✅ DIP Compliant:

interface IKeyboard {} interface IMonitor {} class Keyboard implements IKeyboard {} class Monitor implements IMonitor {} class Computer { private IKeyboard keyboard; private IMonitor monitor; public Computer(IKeyboard keyboard, IMonitor monitor) { this.keyboard = keyboard; this.monitor = monitor; } }

✅ Summary Table

PrincipleFocusBenefit
SRPOne reason to changeSimpler, focused classes
OCPExtend without modifySafer enhancements
LSPSubstitutable subclassesReliable polymorphism
ISPSmall, specific interfacesClean, decoupled code
DIPAbstractions over concretesFlexible, testable design

No comments:

Post a Comment