SOLID Principles Simplified
The SOLID principles are a set of five design principles in object-oriented programming aimed at making software designs more understandable, flexible, and maintainable. The acronym SOLID stands for:
1) Single Responsibility Principle (SRP)
A class should have only one reason to change, meaning it should have only one responsibility.
This promotes maintainability by keeping classes focused and reduces the risk of unintended side effects when making changes.
//Before:
public class UserManager {
public void createUser(User user) {
// Code for creating a user
}
public void sendEmail(User user) {
// Code for sending email to user
}
public void deleteUser(User user) {
// Code for deleting a user
}
}
//After:
public class UserManager {
public void createUser(User user) {
// Code for creating a user
}
public void deleteUser(User user) {
// Code for deleting a user
}
}
public class EmailService {
public void sendEmail(User user) {
// Code for sending email to user
}
}
2) Open/Closed Principle (OCP)
Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
This allows for easy addition of new features without altering existing code, promoting scalability and minimizing the risk of introducing bugs.
//Before:
public class Shape {
public void draw() {
// Code for drawing shape
}
}
public class Circle extends Shape {
@Override
public void draw() {
// Code for drawing circle
}
}
//After:
public abstract class Shape {
public abstract void draw();
}
public class Circle extends Shape {
@Override
public void draw() {
// Code for drawing circle
}
}
public class Rectangle extends Shape {
@Override
public void draw() {
// Code for drawing rectangle
}
}
3) Liskov Substitution Principle (LSP)
Objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program.
It ensures that code behaves as expected when using polymorphism, promoting code reliability since we don’t have to type check again and again.
//Before:
public class Bird {
public void fly() {
// Code for flying
}
}
public class Ostrich extends Bird {
@Override
public void fly() {
// Ostrich cannot fly
}
}
//After
public interface Bird {
void fly();
}
public class Sparrow implements Bird {
@Override
public void fly() {
// Code for flying
}
}
public class Ostrich implements Bird {
@Override
public void fly() {
// Ostrich cannot fly
}
}
Confused about why OCP and LSP seem to be the same in this example? Refer this article in stackoverflow to clear your doubts: https://softwareengineering.stackexchange.com/questions/178488/lsp-vs-ocp-liskov-substitution-vs-open-close#178515
4) Interface Segregation Principle (ISP)
Clients should not be forced to depend on interfaces they don't use. Instead, interfaces should be specific to the needs of the clients.
Suppose you're developing a media player application. Instead of having a single
MediaPlayer
interface with methods for playing audio, video, and streaming, you can segregate these into separate interfaces likeAudioPlayer
,VideoPlayer
, andStreamPlayer
. This allows clients to implement only the interfaces they need, reducing unnecessary dependencies and promoting cleaner design.
// Before applying ISP
interface Worker {
void work();
void eat();
}
class WorkerImpl implements Worker {
public void work() {
// implementation of working
System.out.println("Working...");
}
public void eat() {
// implementation of eating
System.out.println("Eating...");
}
}
class SuperWorker implements Worker {
public void work() {
// implementation of working
System.out.println("Working hard...");
}
public void eat() {
// implementation of eating
System.out.println("Eating...");
}
}
class Manager {
private Worker worker;
public void setWorker(Worker w) {
worker = w;
}
public void manage() {
worker.work();
}
}
// After applying ISP
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class Worker implements Workable, Eatable {
public void work() {
System.out.println("Working...");
}
public void eat() {
System.out.println("Eating...");
}
}
class Robot implements Workable {
public void work() {
System.out.println("Working hard...");
}
}
class Manager {
private Workable worker;
public void setWorker(Workable w) {
worker = w;
}
public void manage() {
worker.work();
}
}
5) Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details; details should depend on abstractions.
High-level modules contain the important policy decisions and business models of an application – The identity of the application. Low-level modules contain detailed implementations of individual mechanisms needed to realize the policy.
// Before applying DIP
class LightBulb { //Low level
public void turnOn() {
System.out.println("LightBulb: Bulb turned on...");
}
public void turnOff() {
System.out.println("LightBulb: Bulb turned off...");
}
}
class Switch { //manages operations & relies of LightBulb to perform functions:High Level
private LightBulb bulb;
public Switch(LightBulb bulb) {
this.bulb = bulb;
}
public void operate(String command) {
if (command.equals("ON")) {
bulb.turnOn();
} else if (command.equals("OFF")) {
bulb.turnOff();
}
}
}
// After applying DIP
interface Switchable {
void turnOn();
void turnOff();
}
class LightBulb implements Switchable {
public void turnOn() {
System.out.println("LightBulb: Bulb turned on...");
}
public void turnOff() {
System.out.println("LightBulb: Bulb turned off...");
}
}
class Fan implements Switchable {
public void turnOn() {
System.out.println("Fan: Fan turned on...");
}
public void turnOff() {
System.out.println("Fan: Fan turned off...");
}
}
class Switch { //now it depends upon Switchable interface instead of concrete implementations (LightBulb, Fan)
private Switchable device;
public Switch(Switchable device) {
this.device = device;
}
public void operate(String command) {
if (command.equals("ON")) {
device.turnOn();
} else if (command.equals("OFF")) {
device.turnOff();
}
}
}
Resources :-
"Clean Code: A Handbook of Agile Software Craftsmanship" by Robert C. Martin, commonly known as "Uncle Bob".
This freecodecamp article : https://www.freecodecamp.org/news/solid-design-principles-in-software-development/
Subscribe to my newsletter
Read articles from Parth Agarwal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Parth Agarwal
Parth Agarwal
Hey everyone! As a Computer Science undergrad from Kanpur, India, I'm excited to be part of this community. I'm here to both contribute from my learnings and gain wisdom from the diverse perspectives shared by others.