Blog Datasheets Home About me Clients My work Services Contact

G2Labs Grzegorz Grzęda

Exploring the Single Responsibility Principle in depth

March 14, 2023

In the world of software development, the Single Responsibility Principle (SRP) is a fundamental concept that plays a crucial role in designing maintainable and scalable code. SRP, one of the five SOLID principles, emphasizes that a class should have only one reason to change, meaning that it should only have one responsibility. In this blog post, we will delve into the depths of the Single Responsibility Principle, explore its significance, and demonstrate its application using extensive examples in both C and Python.

Understanding the Single Responsibility Principle

The Single Responsibility Principle advocates for a high level of cohesion within classes and modules, ensuring that they encapsulate a single, well-defined responsibility. By adhering to SRP, code becomes more modular, easier to understand, maintain, test, and extend. This principle promotes the separation of concerns, reducing the potential for ripple effects when making changes and facilitating code reusability.

Example in C

Let’s consider a simple example in C to illustrate the Single Responsibility Principle. Suppose we have a file employee.c containing the implementation of an Employee struct with various functionalities.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// employee.c

#include <stdio.h>

typedef struct {
    char name[50];
    int age;
} Employee;

void saveEmployeeData(Employee emp) {
    // Saving employee data to file
    FILE *file = fopen("employee_data.txt", "w");
    fprintf(file, "Name: %s, Age: %d", emp.name, emp.age);
    fclose(file);
}

void printEmployeeDetails(Employee emp) {
    // Printing employee details
    printf("Name: %s, Age: %d", emp.name, emp.age);
}

In this example, the employee.c file contains both the saveEmployeeData and printEmployeeDetails functions. However, according to the Single Responsibility Principle, these two responsibilities should ideally be encapsulated in separate modules or functions to adhere to SRP.

To adhere to SRP, let’s restructure the employee.c file to separate the responsibilities:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// employee_storage.c

#include <stdio.h>

typedef struct {
    char name[50];
    int age;
} Employee;

void saveEmployeeData(Employee emp) {
    // Saving employee data to file
    FILE *file = fopen("employee_data.txt", "w");
    fprintf(file, "Name: %s, Age: %d", emp.name, emp.age);
    fclose(file);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// employee_printer.c

#include <stdio.h>

typedef struct {
    char name[50];
    int age;
} Employee;

void printEmployeeDetails(Employee emp) {
    // Printing employee details
    printf("Name: %s, Age: %d", emp.name, emp.age);
}

In this restructured example, the responsibilities related to saving and printing employee data have been separated into distinct modules, adhering to the Single Responsibility Principle.

Example in Python

Now, let’s shift our focus to Python and explore the Single Responsibility Principle through a practical example. Consider a Python class Order that handles both order processing and order status tracking.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Order:
    def __init__(self, order_id, order_details):
        self.order_id = order_id
        self.order_details = order_details

    def process_order(self):
        # Process the order and update inventory
        # ...

    def track_order_status(self):
        # Track the status of the order
        # ...

In this example, the Order class is responsible for both processing orders and tracking order status, violating the Single Responsibility Principle. To adhere to SRP, we can separate the responsibilities as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class OrderProcessor:
    def __init__(self, order_id, order_details):
        self.order_id = order_id
        self.order_details = order_details

    def process_order(self):
        # Process the order and update inventory
        # ...


class OrderStatusTracker:
    def __init__(self, order_id):
        self.order_id = order_id

    def track_order_status(self):
        # Track the status of the order
        # ...

In this refactored example, the responsibilities related to order processing and order status tracking have been encapsulated in separate classes, aligning with the Single Responsibility Principle.

Conclusion

In conclusion, the Single Responsibility Principle serves as a guiding principle for writing maintainable and cohesive code. By adhering to SRP, developers can achieve better code organization, improved testability, and easier maintenance. Through the examples in both C and Python, we have explored the application of SRP, emphasizing the importance of separating concerns and encapsulating distinct responsibilities within classes and modules. By understanding and applying the Single Responsibility Principle, developers can contribute to the creation of robust and scalable software systems.

I hope this blog post has provided you with a comprehensive understanding of the Single Responsibility Principle and its application in C and Python. As always, striving for clean, modular, and maintainable code should be a fundamental goal for every programmer. Thank you for reading!


➡️ Solid principles


⬅️ Understanding Data Sizes in C: Insights for the Embedded Programme


Go back to Posts.