SOLID Design Principles in Software Development

The SOLID design principles are a set of five principles that help developers create software that is easy to use, maintain, and extend.
S- Single Responsibility Principle(SRP)
O- Open/Closed Principle(OCP)
L-Liskov Substitution Principle(LSP)
I-Interface Segregation Principle(ISP)
D-Dependency Ineversion Principle(DIP)
Single Responsibility Principle (SRP)
There should be only one reason to change any class , module and function.It means every class, module and function should have only one responsibility.
Example:
Without SRP
package SOLID.SRP.WOT;
class Employee {
private String name;
private double hourlyRate;
private int hoursWorked;
public Employee(String name, double hourlyRate, int hoursWorked) {
this.name = name;
this.hourlyRate = hourlyRate;
this.hoursWorked = hoursWorked;
}
public double calculateSalary() {
return hourlyRate * hoursWorked;
}
public void saveToDatabase() {
System.out.println("Saving " + name + " to database...");
}
}
Here, Employee class has not only employee data but it is also calculating salary and saving data to database.
Employee class has multiple responsibility that’s why violates SRP.
With SRP
package SOLID.SRP.WT;
public class Employee {
private String name;
private double hourlyRate;
private int totalHours;
Employee(String name, double hourlyRate) {
this.name = name;
this.hourlyRate = hourlyRate;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setHourlyRate(double hourlyRate) {
this.hourlyRate = hourlyRate;
}
public double getHourlyRate() {
return hourlyRate;
}
public void setTotalHours(int totalHours) {
this.totalHours = totalHours;
}
public int getTotalHours() {
return totalHours;
}
}
package SOLID.SRP.WT;
public class SalaryCalculation {
public double calculateSalary(Employee employee) {
return employee.getHourlyRate()*employee.getTotalHours();
}
}
package SOLID.SRP.WT;
public class SaveEmployeeToDB {
public void saveToDatabase(Employee employee) {
System.out.println("Saving " + employee.getName() + " to database...");
}
}
here Every class has only one responsibility , these classes supporting SRP.
Open/Closed Principle(OCP)
A class should be open for extension but closed for modification.
Simple Meaning:
You should be able to add new features without changing existing code.
Instead of modifying a class, you should extend it using inheritance or polymorphism.
Violates OCP
package SOLID.OCP.ViolateOCP;
class PaymentProcessor {
public void processPayment(String paymentType, double amount) {
if (paymentType.equals("CreditCard")) {
System.out.println("Processing credit card payment of $" + amount);
} else if (paymentType.equals("PayPal")) {
System.out.println("Processing PayPal payment of $" + amount);
}
}
}
If we want to add any other payment method then we have to modify this PaymentProcessor class.
Follows OCP
package SOLID.OCP.FollowOCP;
public interface PaymentMethod {
public void payment(double amount);
}
package SOLID.OCP.FollowOCP;
public class CreditCardPayment implements PaymentMethod{
@Override
public void payment(double amount) {
System.out.println("Processing credit card payment of Rs." + amount);
}
}
package SOLID.OCP.FollowOCP;
public class DebitCardPayment implements PaymentMethod{
@Override
public void payment(double amount) {
System.out.println("Processing debit card payment of Rs." + amount);
}
}
package SOLID.OCP.FollowOCP;
public class PaymentProcessor {
public void processPayment(PaymentMethod paymentMethod, double amount) {
paymentMethod.payment(amount);
}
}
package SOLID.OCP.FollowOCP;
public class Main {
public static void main(String[] args) {
DebitCardPayment debitCardPayment = new DebitCardPayment();
CreditCardPayment creditCardPayment = new CreditCardPayment();
PaymentProcessor processor = new PaymentProcessor();
processor.processPayment(debitCardPayment,200.0);
}
}
Here we have two types of payment method debit and credit .
suppose we want to add a new payment method that is Upi payment method.
we just need to create a new class that will implement PaymentMethod interface just like DebitCardPayment and CreditCardPayment
package SOLID.OCP.FollowOCP;
public class UPIPayment implements PaymentMethod{
@Override
public void payment(double amount) {
System.out.println("Processing upi payment of Rs." + amount);
}
}
Here We didn’t modify any class just extended PaymentMethod class.
Liskov Substitution Principle(LSP)
A child class must be able to replace its parent class without breaking the functionality of the program.
Key Points:
A subclass should be able to replace a superclass without issues.
If a child class doesn’t fully behave like its parent, refactor the hierarchy!
Use interfaces and separate behaviors when needed.
Violates LSP
package SOLID.LSP.Violate;
public class Vehicle {
public void startEngine() {
System.out.println("Engine has started!");
}
}
package SOLID.LSP.Violate;
public class Car extends Vehicle{
}
package SOLID.LSP.Violate;
public class Cycle extends Vehicle{
}
package SOLID.LSP.Violate;
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.startEngine();
Cycle cycle = new Cycle();
cycle.startEngine(); // Cycle does NOT have an engine, but forced to implement startEngine()
}
}
Follows LSP
package SOLID.LSP.Follow;
public class Vehicle {
public void move() {
System.out.println("Vehicle is moving....");
}
}
package SOLID.LSP.Follow;
public interface EnginePowered {
public void startEnginee();
}
package SOLID.LSP.Follow;
import SOLID.LSP.Follow.Vehicle;
public class Car extends Vehicle implements EnginePowered {
@Override
public void startEnginee() {
System.out.println("Engine has started....");
}
}
package SOLID.LSP.Follow;
import SOLID.LSP.Follow.Vehicle;
public class Cycle extends Vehicle {
}
package SOLID.LSP.Follow;
import SOLID.LSP.Follow.Car;
import SOLID.LSP.Follow.Cycle;
import SOLID.LSP.Follow.Vehicle;
public class Main {
public static void main(String[] args) {
Car car = new Car();
Cycle cycle = new Cycle();
car.move();
car.startEnginee();
cycle.move(); //cycle can't call startEngine which is correct
}
}
Interface Segregation Principle(ISP)
What is ISP?
A class should not be forced to implement interfaces it does not use.
Instead of one large interface, break it into smaller, specific interfaces.
A class should only depend on methods that are relevant to it.
Use multiple interfaces for different behaviors.
Violates ISP
package SOLID.ISP.Violates;
public interface Printer {
public void print();
public void scan();
public void fax();
}
package SOLID.ISP.Violates;
public class BasicPrinter implements Printer{
@Override
public void print() {
System.out.println("Basic Printing.....");
}
@Override
public void scan() {
System.out.println("Basic Scanning....."); // doesn't support still have to implement
}
@Override
public void fax() {
System.out.println("Basic Faxing....."); // doesn't support still have to implement
}
}
package SOLID.ISP.Violates;
public class AdvancePrinter implements Printer{
@Override
public void print() {
System.out.println("Advanced Printing.....");
}
@Override
public void scan() {
System.out.println("Advanced Scanning.....");
}
@Override
public void fax() {
System.out.println("Advanced Faxing.....");
}
}
package SOLID.ISP.Violates;
public class Main {
public static void main(String[] args) {
BasicPrinter basicPrinter = new BasicPrinter();
AdvancePrinter advancePrinter = new AdvancePrinter();
basicPrinter.print();
basicPrinter.scan(); // but this printer doesn't support this functionality
basicPrinter.fax(); // but this printer doesn't support this functionality
}
}
Here basic printer doesn’t support scan and fax still , still it has to implement these functions which violates ISP.
Follows ISP
package SOLID.ISP.Follows;
public interface Printable {
public void print();
}
package SOLID.ISP.Follows;
public interface Scanable {
public void scan();
}
package SOLID.ISP.Follows;
public interface Faxable {
public void fax();
}
package SOLID.ISP.Follows;
public class BasicPrinter implements Printable{
@Override
public void print() {
System.out.println("Basic Printing....");
}
}
package SOLID.ISP.Follows;
import SOLID.ISP.Violates.Printer;
public class AdvancePrinter implements Printable,Scanable,Faxable {
@Override
public void print() {
System.out.println("Advance Printing....");
}
@Override
public void scan() {
System.out.println("Advance Scanning....");
}
@Override
public void fax() {
System.out.println("Advance faxing.....");
}
}
package SOLID.ISP.Follows;
public class Main {
public static void main(String[] args) {
BasicPrinter basicPrinter = new BasicPrinter();
AdvancePrinter advancePrinter = new AdvancePrinter();
basicPrinter.print();
advancePrinter.fax();
advancePrinter.print();
advancePrinter.scan();
}
}
Here BasicPrinter only supports printing. it has not any other unnecessary functions.
Dependency Ineversion Principle(DIP)
What is DIP?
High-level modules should not depend on low-level modules.
Both should depend on abstractions (interfaces).
Abstractions should not depend on details. Details should depend on abstractions.(This means high-level code (business logic) should depend on interfaces or abstract classes, not on concrete implementations
.)
Why is DIP important?
If high-level modules directly depend on low-level modules, code becomes tightly coupled.
Changing one part of the system can break other parts.
DIP makes the code flexible and easy to maintain.
Higher-level and lower-level classes should depend on interfaces or abstract classes instead of concrete implementations.
Violates DIP
package SOLID.DIP.Violates;
// Low-Level-class
public class PaypalPayment {
public void pay(double amount) {
System.out.println("Payment is getting Processed RS." + amount);
}
}
package SOLID.DIP.Violates;
// Order-service is high level module
public class OrderService {
public PaypalPayment paypalPayment;
public OrderService() {
this.paypalPayment= new PaypalPayment();
}
public void processOrder(double amount) {
paypalPayment.pay(amount);
System.out.println("Order processed!");
}
}
package SOLID.DIP.Violates;
public class Main {
public static void main(String[] args) {
OrderService orderService = new OrderService();
orderService.processOrder(700);
}
}
here OrderService
is tightly coupled with PayPalPayment
.
- Adding new payment methods (Stripe, Razorpay) requires modifying
OrderService
.
Follows DIP
package SOLID.DIP.Follows;
public interface PaymentMethod {
public void pay(double amount);
}
package SOLID.DIP.Follows;
public class PayPalPayment implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("Payment is getting Processed through PayPal RS." + amount);
}
}
package SOLID.DIP.Follows;
public class UPI implements PaymentMethod{
@Override
public void pay(double amount) {
System.out.println("Payment is getting Processed through UPI RS." + amount);
}
}
package SOLID.DIP.Follows;
import SOLID.DIP.Follows.PaymentMethod;
public class OrderService {
public PaymentMethod paymentProcessor;
public OrderService( PaymentMethod paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void processOrder(double amount) {
paymentProcessor.pay(amount);
System.out.println("Order processed!");
}
}
package SOLID.DIP.Follows;
import SOLID.DIP.Follows.OrderService;
public class Main {
public static void main(String[] args) {
OrderService orderService = new OrderService(new UPI());
orderService.processOrder(300);
orderService = new OrderService(new PayPalPayment());
orderService.processOrder(200);
}
}
Why This is Better?
1.OrderService does not depend on PayPal or UPI. It depends on the PaymentMethod abstraction.
2.Adding new payment methods does not require modifying OrderService
.
3.More flexible, reusable, and maintainable design.
Subscribe to my newsletter
Read articles from Nivedita Kumari directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Nivedita Kumari
Nivedita Kumari
I am wokring as SDE-1 at BetterPlace