Single Responsibility Principle

1 min read
On trails:CSSOLIDstep 2/6

A class should have only one reason to change. That means a class should have a single, well-defined responsibility or purpose within the system.

In C#

An OrderProcessor class that handles order validation, payment processing and inventory updates violates SRP. A better design separates these concerns into distinct classes: OrderValidator, PaymentService and InventoryManager.

First, the violation. One class does all four jobs.

C#
// Violation of SRPpublic class OrderProcessor_Bad{    public void ProcessOrder(Order order)    {        ValidateOrder(order);        TakePayment(order.PaymentDetails);        UpdateInventory(order.OrderItems);        SendConfirmationEmail(order.CustomerEmail);    }    private void ValidateOrder(Order order) { /*... */ }    private void TakePayment(PaymentDetails details) { /*... */ }    private void UpdateInventory(List<OrderItem> items) { /*... */ }    private void SendConfirmationEmail(string email) { /*... */ }}

Then the version that follows SRP. There is one interface per responsibility, and OrderProcessor_Good only coordinates them.

C#
// Adherence to SRPpublic interface IOrderValidator { bool Validate(Order order); }public interface IPaymentService { bool ProcessPayment(PaymentDetails details); }public interface IInventoryService { void UpdateStock(List<OrderItem> items); }public interface INotificationService { void SendOrderConfirmation(string email); } public class OrderProcessor_Good{    private readonly IOrderValidator _validator;    private readonly IPaymentService _paymentService;    //... other services     public OrderProcessor_Good(IOrderValidator validator, IPaymentService paymentService /*...*/)    {        _validator = validator;        _paymentService = paymentService;        //...    }     public void ProcessOrder(Order order)    {        if (!_validator.Validate(order)) throw new Exception("Invalid order");        if (!_paymentService.ProcessPayment(order.PaymentDetails)) throw new Exception("Payment failed");        //... update inventory, send email using their respective services    }}

Where it matters

SRP leads to classes that are more cohesive, smaller, and easier to understand, test and maintain. Changes related to one responsibility are isolated, and less likely to affect other parts of the system by accident.

It’s important to apply SRP pragmatically, though. Over-granularity can lead to an excessive number of small classes, which increases the complexity of managing their interactions. The “reason to change” should be defined at an appropriate level of abstraction for the domain.


Sources