26 Oct 2023



Intermediate

The Single Responsibility Principle states that each software module or class should have only one reason to change, meaning that a class should have only one primary responsibility or job.

In simpler terms, it means that each part of your code (like a function or a class) should have one specific job or responsibility, and it should do that job well.


Real world example:

Imagine you have a toolbox. Each tool in the toolbox has a specific purpose, like a hammer for pounding nails or a screwdriver for turning screws. The hammer doesn't try to be a screwdriver, and the screwdriver doesn't try to be a hammer. They have a single responsibility.

In software, it's the same idea. If you're writing a class or a function, it should do one thing and do it well. This makes your code easier to understand, maintain, and change in the future. If you follow the Single Responsibility Principle, it's like having a well-organized toolbox for your code, with each piece doing its job without trying to do too much.


Here are some examples of problems and their solutions using the Single Responsibility Principle (SRP):

Problem 1: In a software application, you have a User class that handles user authentication, user data storage, and user profile management. This class has become large and difficult to maintain.

class User:
    - username
    - password
    - userData
    - userPreferences

    + authenticate()
    + saveUserData()
    + updateUserPreferences()

Solution 1: Apply SRP to split the User class into three separate classes, each with a single responsibility:

  1. UserAuthentication: This class is responsible for authenticating users, handling login and logout operations, and managing access control. This separation keeps the authentication logic self-contained and allows for easier maintenance and changes to authentication procedures without affecting other aspects of user management.

  2. UserDataStorage: This class handles the storage of user data in a database or any other data store. It includes methods for saving, retrieving, and updating user data. By separating this responsibility, you can change the data storage mechanism independently of the authentication or user profile management.

class UserAuthentication:
    - username
    - password

    + authenticate()

class UserDataStorage:
    - userData

    + saveUserData()

class UserProfileManager:
    - userPreferences

    + updateUserPreferences()

Problem 2: You have a Message class that is responsible for both formatting a message and sending it via email. This class combines two distinct responsibilities, making it harder to maintain and less flexible.

class Message:
    - messageContent
    - recipient

    + formatMessage()
    + sendEmail()

Solution 2: Apply SRP by creating two separate classes:

  1. MessageFormatter: This class is responsible for formatting messages. It takes plain text or data and converts it into a formatted message, ready for sending. Separating this responsibility allows you to modify the message format without affecting the email sending logic.

  2. EmailSender: This class is responsible for sending messages via email. It accepts a formatted message and handles the email delivery process. By separating this responsibility, you can change the email sending method (e.g., switch from SMTP to a third-party service) without impacting the message formatting.

class MessageFormatter:
    - messageContent

    + formatMessage()

class EmailSender:
    - formattedMessage
    - recipient

    + sendEmail()

Problem 3: You have a FileManipulator class that reads data from a file and performs data processing operations, such as sorting and filtering. This class is becoming difficult to maintain because it combines file I/O and data processing responsibilities.

class FileManipulator:
    - fileName

    + openFile()
    + readFile()
    + processData()
    + closeFile()

Solution 3: Apply SRP by creating two separate classes:

  1. FileReader: This class is responsible for reading data from files. It provides methods for opening, reading, and closing files. Separating this responsibility allows you to change the file reading method or handle different file formats without affecting data processing.

  2. DataProcessor: This class is responsible for data manipulation and processing. It takes data from a FileReader and performs sorting, filtering, or any other data-related operations. Separating this responsibility makes it easier to modify data processing algorithms independently of file reading.

class FileReader:
    - fileName

    + openFile()
    + readFile()
    + closeFile()

class DataProcessor:
    - rawData

    + processData()

Problem 4: You have a Customer class that handles both customer data management and order processing. This violates SRP because customer data management and order processing are separate concerns.

class Customer:
    - customerData
    - orders

    + createCustomer()
    + updateCustomer()
    + deleteCustomer()
    + processOrder()
    + calculateOrderTotal()

Solution 4: Apply SRP by creating two separate classes:

  1. CustomerManager: This class is responsible for customer data management, such as creating, updating, and deleting customer records. It maintains the customer database and handles customer-related operations.

  2. OrderProcessor: This class is responsible for processing orders, managing the order workflow, and calculating order-related data. Separating this responsibility allows you to make changes to order processing without affecting customer data management.

class CustomerManager:
    - customerData

    + createCustomer()
    + updateCustomer()
    + deleteCustomer()

class OrderProcessor:
    - orders

    + processOrder()
    + calculateOrderTotal()

In each of these examples, applying the Single Responsibility Principle results in more modular and maintainable code, as each class has a well-defined and singular responsibility. It becomes easier to extend or modify the system because changes to one responsibility do not ripple through the entire class.

solid-principles
single-responsibility-principle