Skip to main content

Single Responsibility Principle – Simple Explanation with Example

When writing code, it’s common to put multiple responsibilities inside a single file or class. It might work at first, but over time it becomes difficult to manage and update.

The Single Responsibility Principle (SRP) helps solve this.

It is one of the core ideas from SOLID and focuses on keeping code simple and maintainable.

Single Responsibility Principle

What is Single Responsibility Principle?

The idea is simple:

A class should have only one responsibility.

That means:

  • It should do one job
  • It should have only one reason to change

Why This Matters

When a class does too many things:

  • Changes become risky
  • Bugs become harder to track
  • Code becomes difficult to understand

Keeping responsibilities separate makes your code:

  • Cleaner
  • Easier to maintain
  • Easier to scale

Bad Example (Multiple Responsibilities in One Class)

Let’s look at a class that handles everything:


class UserService {
    validateUser(user) {
        if (!user.email) {
            console.log("Invalid user");
            return false;
        }
        return true;
    }

    saveUser(user) {
        console.log("Saving user to database");
    }

    sendEmail(user) {
        console.log("Sending welcome email");
    }

    processUser(user) {
        if (!this.validateUser(user)) return;

        this.saveUser(user);
        this.sendEmail(user);
    }
}

Problem here:

  • This class is handling validation
  • It is saving data
  • It is also sending emails

So if any of these responsibilities change, we must modify the same class.

Good Example (Single Responsibility per Class)

Now let’s split responsibilities into separate classes:


class UserValidator {
    validate(user) {
        return user.email ? true : false;
    }
}

class UserRepository {
    save(user) {
        console.log("Saving user to database");
    }
}

class EmailService {
    send(user) {
        console.log("Sending welcome email");
    }
}

Now we use them together:


class UserService {
    constructor() {
        this.validator = new UserValidator();
        this.repository = new UserRepository();
        this.emailService = new EmailService();
    }

    processUser(user) {
        if (!this.validator.validate(user)) return;

        this.repository.save(user);
        this.emailService.send(user);
    }
}

Why this is better:

  • Each class has only one responsibility
  • Changes in one part do not affect others
  • Code becomes easier to test and maintain

Going One Step Further (Better Design)

In real-world applications, we can improve this design even further.

Instead of validating data separately, we can ensure that a valid object is created from the beginning. This approach is often called "Parse, don't validate".

Let’s look at an improved version:


// User class ensures valid data during creation
class User {
    constructor(userData) {
        if (!userData.email) {
            throw new Error("Invalid User: Email is required.");
        }
        this.email = userData.email;
    }
}

// Handles only database operations
class UserRepository {
    save(user) {
        console.log(`Saving ${user.email} to the database...`);
    }
}

// Handles only email sending
class EmailService {
    sendWelcome(user) {
        console.log(`Sending welcome email to ${user.email}`);
    }
}

// Coordinates the flow
class UserRegistrationService {
    constructor(repository, emailService) {
        this.repository = repository;
        this.emailService = emailService;
    }

    register(userData) {
        try {
            const user = new User(userData);

            this.repository.save(user);
            this.emailService.sendWelcome(user);
        } catch (error) {
            console.error("Registration failed:", error.message);
        }
    }
}

Why this is better:

  • The User class ensures only valid data exists
  • The repository only handles saving data
  • The email service only handles sending emails
  • The registration service only coordinates the process

This design keeps responsibilities even more clearly separated and makes the system safer and easier to maintain.

Real-Life Analogy

Think of a restaurant:

  • Chef – cooks food
  • Cashier – handles billing
  • Delivery – delivers food

If one person does everything, it becomes messy.

Key Idea to Remember

  • One class → one responsibility
  • Keep responsibilities separate
  • Make changes easier and safer

Conclusion

At the beginning, combining everything in one place might feel faster. But as your project grows, it creates problems.

By following SRP, your code becomes:

  • Easier to read
  • Easier to modify
  • More organized

Comments

Popular posts from this blog

SOLID Principles – Simple Explanation for Beginners

 When I started learning object-oriented programming, I often wrote code that worked—but was hard to maintain or extend later. That’s when I came across the SOLID principles. These are five simple guidelines that help us write code that is: Easy to understand Easy to maintain Easy to scale In this blog, I’ll explain each principle in a simple way so you can understand the idea clearly. What are SOLID Principles? SOLID is a set of five design principles introduced by Robert C. Martin. Each letter represents one principle: S → Single Responsibility O → Open-Closed L → Liskov Substitution I → Interface Segregation D → Dependency Inversion S — Single Responsibility Principle (SRP) A class should have only one responsibility. Simple idea A class should do only one job. If a class has multiple responsibilities: It becomes harder to manage Changes in one part can affect other parts Example thinking Instead of: One class handling user data + logging + validation Split it into: One class fo...

Oops Concepts : Inheritance In Java

When I first started learning Java, inheritance felt a bit confusing. But once I understood the basic idea, it became one of the easiest and most powerful concepts in Object-Oriented Programming (OOP). In this blog, I’ll explain inheritance in very simple English, so even if you're a beginner student or aspiring developer, you can understand it easily.  What is Inheritance in Java? In simple words, inheritance means reusing code from another class. I usually think of it like this: A child inherits features from parents Similarly, a class can inherit properties and methods from another class Definition: Inheritance is a mechanism where a child class gets properties and methods from a parent class. Key Terms (Very Important) Parent Class (Superclass) → The class that provides properties Child Class (Subclass) → The class that inherits those properties How Inheritance Works in Java Java uses the keyword:           extends Basic Syntax: This means the Chil...