SOLID

savitry.in
0

The SOLID principles are five core design principles in object-oriented programming (OOP) that help create code that is:

  • Easy to understand

  • Flexible to change

  • Maintainable over time

They were popularized by Robert C. Martin (Uncle Bob) and are very relevant in Java development.

Let’s go through each principle with a short explanation and a Java-based example:


✅ S – Single Responsibility Principle (SRP)

A class should have only one reason to change.

🔸 Explanation:

Each class should do one thing only and do it well. This makes the class easier to maintain and test.

💡 Java Example:

// ❌ Violates SRP – doing both user logic and saving
class User {
    void register(String username, String password) {
        // registration logic
    }

    void saveToDatabase() {
        // DB saving logic
    }
}

// ✅ Follows SRP – separate responsibilities
class User {
    String username;
    String password;
    // getters/setters
}

class UserService {
    void register(User user) {
        // registration logic
    }
}

class UserRepository {
    void save(User user) {
        // DB saving logic
    }
}

✅ O – Open/Closed Principle (OCP)

Software entities should be open for extension, but closed for modification.

🔸 Explanation:

You should be able to add new functionality without modifying existing code, often using abstraction.

💡 Java Example:

// ❌ Violates OCP – modifying method every time a new shape is added
class AreaCalculator {
    double calculateArea(Object shape) {
        if (shape instanceof Circle) {
            return Math.PI * ((Circle) shape).radius * ((Circle) shape).radius;
        } else if (shape instanceof Square) {
            return ((Square) shape).side * ((Square) shape).side;
        }
        return 0;
    }
}

// ✅ Follows OCP – open for extension via polymorphism
interface Shape {
    double area();
}

class Circle implements Shape {
    double radius;
    public double area() {
        return Math.PI * radius * radius;
    }
}

class Square implements Shape {
    double side;
    public double area() {
        return side * side;
    }
}

class AreaCalculator {
    double calculateArea(Shape shape) {
        return shape.area();
    }
}

✅ L – Liskov Substitution Principle (LSP)

Subclasses should be substitutable for their base classes without breaking the behavior.

🔸 Explanation:

A subclass should honor the contract of the base class. You should be able to use a subclass wherever the parent class is expected.

💡 Java Example:

class Bird {
    void fly() {
        System.out.println("Bird is flying");
    }
}

class Sparrow extends Bird {}

class Ostrich extends Bird {
    @Override
    void fly() {
        throw new UnsupportedOperationException("Ostrich can't fly!");
    }
}
// ❌ Violates LSP – Ostrich breaks the expected behavior of Bird

// ✅ Better approach
interface Bird {}
interface FlyingBird extends Bird {
    void fly();
}

class Sparrow implements FlyingBird {
    public void fly() {
        System.out.println("Sparrow is flying");
    }
}

class Ostrich implements Bird {
    // no fly method – behavior preserved
}

✅ I – Interface Segregation Principle (ISP)

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

🔸 Explanation:

Avoid fat interfaces. Instead, split them into smaller, more specific ones.

💡 Java Example:

// ❌ Violates ISP – PrinterScannerCopier forces all classes to implement unused methods
interface PrinterScannerCopier {
    void print();
    void scan();
    void copy();
}

class SimplePrinter implements PrinterScannerCopier {
    public void print() { }
    public void scan() { throw new UnsupportedOperationException(); }
    public void copy() { throw new UnsupportedOperationException(); }
}

// ✅ Follows ISP – smaller interfaces
interface Printer {
    void print();
}

interface Scanner {
    void scan();
}

class SimplePrinter2 implements Printer {
    public void print() { }
}

✅ D – Dependency Inversion Principle (DIP)

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

🔸 Explanation:

Depend on interfaces, not concrete implementations. It helps with loose coupling.

💡 Java Example:

// ❌ Violates DIP – tightly coupled to EmailService
class EmailService {
    void send(String message) { }
}

class Notification {
    private EmailService emailService = new EmailService();

    void sendAlert(String msg) {
        emailService.send(msg);
    }
}

// ✅ Follows DIP – depends on abstraction
interface MessageService {
    void send(String message);
}

class EmailService implements MessageService {
    public void send(String message) { }
}

class Notification {
    private MessageService service;

    public Notification(MessageService service) {
        this.service = service;
    }

    void sendAlert(String msg) {
        service.send(msg);
    }
}

🧠 Summary Table:

Principle Responsibility
S One reason to change per class
O Extend, don’t modify
L Subclasses must honor base behavior
I Prefer small, focused interfaces
D Rely on abstractions, not concrete classes


Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.
Post a Comment (0)
Our website uses cookies to enhance your experience. Learn More
Accept !