Single Responsibility Principle (SRP)

Last Updated: Jun 2, 2024

Bengali

Explanation of SRP

 

The Single Responsibility Principle (SRP) states that a class should have only one reason to change, meaning it should have only one job or responsibility. This helps in reducing the complexity of code, making it more understandable, maintainable, and scalable. A class focusing on a single task ensures modularity and cohesive design.

 

 

Examples Illustrating SRP in Code

Let's consider a RestaurantManager class that initially handles multiple responsibilities.

 

Bad Example (Violating SRP):

 

public class RestaurantManager
{
    public void TakeOrder(Order order)
    {
        // Logic for taking an order
    }

    public void ProcessPayment(Order order, Payment payment)
    {
        // Logic for processing payment
    }

    public void UpdateInventory(Item item, int quantity)
    {
        // Logic for updating inventory
    }
}

 

In this example, RestaurantManager has multiple responsibilities: taking orders, processing payments, and updating inventory. This violates the SRP as changes in one responsibility could affect the others, making the class harder to maintain and understand.

 

Good Example (Adhering to SRP):

 

public class OrderService
{
    public void TakeOrder(Order order)
    {
        // Logic for taking an order
    }
}

public class PaymentService
{
    public void ProcessPayment(Order order, Payment payment)
    {
        // Logic for processing payment
    }
}

public class InventoryService
{
    public void UpdateInventory(Item item, int quantity)
    {
        // Logic for updating inventory
    }
}

 

In this example, each class has a single responsibility: OrderService handles order taking, PaymentService handles payment processing, and InventoryService handles inventory updates. This adheres to the SRP, making the system more modular and easier to maintain.

 

 

Best Practices for Achieving SRP

  1. Identify Responsibilities:

    • Analyze your classes to identify distinct responsibilities. If a class handles multiple responsibilities, consider splitting it into multiple classes.

       

  2. Keep Classes Focused:

    • Ensure each class is focused on a single task. Small, focused classes are easier to understand and maintain.

       

  3. Use Descriptive Names:

    • Give your classes descriptive names that reflect their single responsibility. This helps in understanding what each class is supposed to do and prevents mixing responsibilities.

       

  4. Delegate Responsibilities:

    • Delegate tasks to other classes when a class starts handling multiple tasks. This helps maintain a single responsibility for each class.

       

  5. Refactor Regularly:

    • Regularly review and refactor your code to ensure classes adhere to SRP. As requirements change, refactoring helps in keeping the design clean and focused.

       

  6. Write Unit Tests:

    • Write unit tests for each class to ensure they handle their specific responsibilities correctly. This practice helps in testing and reinforces single responsibility by isolating functionalities.

       

  7. Use Interfaces and Abstract Classes:

    • Define clear interfaces or abstract classes for different responsibilities. This separation encourages implementing classes to adhere to specific responsibilities.

       

  8. Follow Other SOLID Principles:

    • While focusing on SRP, also consider other SOLID principles as they often complement each other and lead to better overall design.

 

Example with Unit Tests

Let's enhance our example by adding unit tests to ensure each class handles its responsibilities correctly.

 

OrderService.cs

 

public class OrderService
{
    public void TakeOrder(Order order)
    {
        // Logic for taking an order
    }
}

 

PaymentService.cs


public class PaymentService
{
    public void ProcessPayment(Order order, Payment payment)
    {
        // Logic for processing payment
    }
}

 

InventoryService.cs

 

public class InventoryService
{
    public void UpdateInventory(Item item, int quantity)
    {
        // Logic for updating inventory
    }
}

 

Unit Tests

OrderServiceTests.cs

 

using NUnit.Framework;

[TestFixture]
public class OrderServiceTests
{
    [Test]
    public void TakeOrder_ShouldAddOrder()
    {
        var orderService = new OrderService();
        var order = new Order();
        
        orderService.TakeOrder(order);
        
        // Add assertions to verify the order was taken correctly
    }
}

 

 

PaymentServiceTests.cs

 

using NUnit.Framework;

[TestFixture]
public class PaymentServiceTests
{
    [Test]
    public void ProcessPayment_ShouldProcessPaymentSuccessfully()
    {
        var paymentService = new PaymentService();
        var order = new Order();
        var payment = new Payment();
        
        paymentService.ProcessPayment(order, payment);
        
        // Add assertions to verify the payment was processed correctly
    }
}

 

 

InventoryServiceTests.cs

 

using NUnit.Framework;

[TestFixture]
public class InventoryServiceTests
{
    [Test]
    public void UpdateInventory_ShouldUpdateInventorySuccessfully()
    {
        var inventoryService = new InventoryService();
        var item = new Item();
        int quantity = 10;
        
        inventoryService.UpdateInventory(item, quantity);
        
        // Add assertions to verify the inventory was updated correctly
    }
}

 

 

By following these best practices and examples, you can ensure your code adheres to the Single Responsibility Principle, resulting in a more maintainable, scalable, and robust system.

 

 

 


Download our free train tracker application from the Play Store and get prized!

Card Image