Understanding the Single Responsibility Principle (SRP)
Tags: solidprinciples, designpatterns, csharp
Published: 2024-07-10 17:28:30

The `Single Responsibility Principle (SRP)` is a cornerstone of object-oriented design, closely associated with the `SOLID principles`. It emphasizes that **a class should have only one reason to change**, meaning it should have a single responsibility or purpose.

## What is SRP?

The principle asserts that **a class should do only one thing and do it very well**. This doesn't imply that a class should have only one method or behavior, but rather that it should contain a cohesive set of behaviors related to a single responsibility.

Let's explore SRP with an example.

## Code without SRP


![Without SRP](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ms6dhm35o4g0rg1lhhir.png)

It is the code representation of UML diagram in c#:

```csharp
public class Book
{
    public string Title { get; }
    public string Author { get; }

    public Book(string title, string author)
    {
        Title = title;
        Author = author;
    }

    public void Print()
    {
        Console.WriteLine($"Printing Book: {Title} by {Author}");
    }

    public void SaveBook(string filePath)
    {
        Console.WriteLine("Saving book to file: " + filePath);
    }
}

```

## Analyzing the Code

Does the `Book` class adhere to the `SRP`? Let's break down its responsibilities:

- **Representing Book Data:** Properties like `Title` and `Author`.
- **Managing Book Data:** The `SaveBook` method.
- **Printing Book Details:** The `Print` method.

Clearly, the `Book` class handles multiple responsibilities.

## Problems with Multiple Responsibilities

**Note:** _`Change` is the only constant in the software._

You might wonder, what’s the harm in having multiple responsibilities in a single class? Here are some potential issues:

**1. Changing printing format:**
Suppose a client requests a change in the print format. Modifying the `Print` method might inadvertently introduce bugs. Since the `Book` class is used in various parts of the software, these bugs could propagate, making the code difficult to maintain.

**2. Altering the SaveFile logic:**

If the saving logic changes (e.g., saving to a database instead of a file), updating the `SaveBook` method could similarly introduce bugs, affecting all parts of the software that use the `Book` class.

**3. Adding new functionality:**

Adding new features, like `exporting the book to a PDF`, increases the complexity of the `Book` class if it already handles multiple responsibilities.

**4. Testing complexity:**
A class with multiple responsibilities can lead to complex unit tests, as tests must account for various concerns.

## Problems of Not Following SRP

- **Harder to Maintain:** Changes to one responsibility might break others, making the code harder to maintain.

- **Understanding challanges**: New developers might struggle to understand a class with multiple responsibilities.

- **Reduced reusablity**: If you need only the print functionality, you still have to include the save functionality, reducing reusability.

- **Poor Testability:** Testing becomes more difficult as tests need to cover multiple responsibilities.

## Refactored Example Following SRP

We can seperate the responsibilities into different classes.

- `BookClass` for managing book details.
- `BookPrinter` for printing the book.
- `BookRepository` for saving the book


![WithSRP](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4x41qctwa9b3hq8hd57.png)

Here is the refactored code.

```csharp
// Book.cs
public class Book
{
    public string Title { get; set; }
    public string Author { get; set; }
}

// BookPrinter.cs
public class BookPrinter
{
    public void Print(Book book)
    {
        Console.WriteLine($"Printing Book: {book.Title} by {book.Author}");
    }
}

// BookRepository.cs
public class BookRepository
{
    public void SaveBook(Book book, string filePath)
    {
        Console.WriteLine("Saving book to file: " + filePath);
    }
}
```

By seperating these responsibilities, each class now has single reason to change:

- The `Book` class will change if the structure of book detail changes.
- The `BookPrinter` class will change if the printing logic changes.
- The `BookRepository` class will change if the saving logic changes.

This makes the code more maintainable, testable, and adheres to the Single Responsibility Principle.