Posted on: January 18, 2025 Posted by: rahulgite Comments: 0

SOLID is a set of five design principles to make software maintainable, scalable, flexible, and testable.


S — Single Responsibility Principle (SRP)

A class should have only one reason to change.

Before (Violation)

public class BankAccountService {
    public void deposit(String accountId, double amount) { /* logic */ }
    public void withdraw(String accountId, double amount) { /* logic */ }

    // VIOLATION: Logging is mixed with business logic
    public void logTransaction(String message) {
        System.out.println("LOG: " + message);
    }
}

Problem: The class handles both banking operations and logging.

After (SRP Applied)

public class BankAccountService {
    private TransactionLogger logger;

    public void deposit(String accountId, double amount) {
        // business logic
        logger.log("Deposited: " + amount);
    }
}

public class TransactionLogger {
    public void log(String message) {
        System.out.println("LOG: " + message);
    }
}

Now: One class handles account operations, another handles logging.

💼 Banking Example Use Case

  • BankAccountService → handles money operations
  • TransactionLogger → logging
  • NotificationService → send SMS/email

Each has a single job.


O — Open/Closed Principle (OCP)

Classes should be open for extension but closed for modification.

Before (Violation)

public class LoanInterestCalculator {
    public double calculateInterest(String loanType, double amount) {
        if (loanType.equals("HOME")) return amount * 0.08;
        if (loanType.equals("PERSONAL")) return amount * 0.13;
        if (loanType.equals("CAR")) return amount * 0.09;
        return 0;
    }
}

Problem: Adding new loan type requires modifying the class.

After (OCP Applied)

public interface InterestPolicy {
    double calculate(double amount);
}

public class HomeLoanInterest implements InterestPolicy {
    public double calculate(double amount) { return amount * 0.08; }
}

public class PersonalLoanInterest implements InterestPolicy {
    public double calculate(double amount) { return amount * 0.13; }
}

public class LoanInterestCalculator {
    public double compute(InterestPolicy policy, double amount) {
        return policy.calculate(amount);
    }
}

Now: Add new loan type → create new class (no modification required).

💼 Banking Example Use Case

  • Home Loan
  • Personal Loan
  • Car Loan
  • Business Loan

All follow the same interface.


L — Liskov Substitution Principle (LSP)

Subclasses should be substitutable for their base classes.

Before (Violation)

public class BankCard {
    public void withdraw(double amount) {}
}

public class CreditCard extends BankCard {
    public void withdraw(double amount) { /* OK */ }
}

public class DebitCard extends BankCard {
    public void withdraw(double amount) { /* OK */ }
}

public class PrepaidCard extends BankCard {
    // VIOLATION: prepaid card cannot withdraw if top-up not done
    public void withdraw(double amount) {
        throw new UnsupportedOperationException("Top-up required");
    }
}

Problem: A subtype is breaking the parent behavior.

After (LSP Applied)

public interface WithdrawableCard {
    void withdraw(double amount);
}

public interface RechargeableCard {
    void recharge(double amount);
}

public class CreditCard implements WithdrawableCard { /* ... */ }
public class DebitCard implements WithdrawableCard { /* ... */ }
public class PrepaidCard implements RechargeableCard { /* ... */ }

Now: Types are replaced with proper interfaces based on behavior.

💼 Banking Example Use Case

  • CreditCard, DebitCard → Withdraw functionality
  • PrepaidCard → Recharge functionality

Avoid forcing features that certain cards do not support.


I — Interface Segregation Principle (ISP)

Clients should not be forced to implement methods they don’t use.

Before (Violation)

public interface BankOperations {
    void deposit();
    void withdraw();
    void getLoan();
    void calculateInterest();
}

public class FixedDepositService implements BankOperations {
    public void deposit() {}
    public void withdraw() { throw new UnsupportedOperationException(); }
    public void getLoan() { throw new UnsupportedOperationException(); }
    public void calculateInterest() {}
}

Problem: FD account does not support withdraw or loans.

After (ISP Applied)

public interface DepositAccount {
    void deposit();
}

public interface InterestBearing {
    void calculateInterest();
}

public class FixedDepositService implements DepositAccount, InterestBearing {
    public void deposit() {}
    public void calculateInterest() {}
}

💼 Banking Example Use Case

  • Savings account uses: deposit, withdraw
  • Fixed Deposit uses: deposit, interest
  • Loan account uses: loan, EMI calculation

Each gets its own interface.


D — Dependency Inversion Principle (DIP)

High-level modules should depend on abstractions, not concrete classes.

Before (Violation)

public class PaymentService {
    private HdfcBankGateway gateway = new HdfcBankGateway();

    public void sendPayment(double amount) {
        gateway.transfer(amount);
    }
}

Problem: Hard-coded dependency → cannot switch to SBI/ICICI.

After (DIP Applied)

public interface BankGateway {
    void transfer(double amount);
}

public class HdfcBankGateway implements BankGateway {
    public void transfer(double amount) {}
}

public class PaymentService {
    private BankGateway gateway;

    public PaymentService(BankGateway gateway) {
        this.gateway = gateway;
    }

    public void sendPayment(double amount) {
        gateway.transfer(amount);
    }
}

Now: High-level class depends on interface, not implementation.

💼 Banking Example Use Case

  • PaymentService works with any bank gateway (SBI, ICICI, HDFC, etc.)
  • Easy to extend → plug new banks

📌 Summary Table

PrincipleMeaningBanking Example
SRPOne class = one responsibilityAccountService vs Logger
OCPAdd new behavior without modifying existing codeNew loan types
LSPSubtypes must behave like base typesCard types
ISPSmall specialized interfacesFD, Loan, Savings accounts
DIPDepend on abstractionsBank Gateway Interface

📘 Notes / Key Takeaways

  • Follow SOLID to avoid tightly coupled code.
  • Banking apps change frequently → SOLID ensures scalability.
  • Most violations occur when adding new features quickly.
  • DIP + OCP are the most important for real banking systems.
  • Use interfaces, avoid concrete bindings.
  • Prefer compo

Leave a Comment