Weekly Dev Tips

Single Responsibility Principle

Episode Summary

This is episode 49 on the Single Responsibility Principle.

Episode Notes

Hi and welcome back to Weekly Dev Tips. I’m your host Steve Smith, aka Ardalis.

This is episode 49 on the Single Responsibility Principle.

Single Responsibility

This week's tip is brought to you by devBetter.com.

Sponsor - devBetter Group Career Coaching for Developers

Are you a software developer looking to advance in your career more quickly? Would you find a mentor and a group of like-minded professionals valuable? If so, check out devBetter.com and read the testimonials at the bottom of the page. Sign up for a risk free membership if you're interested in growing your network and skills with us.

Show Notes / Transcript

The Single Responsibility Principle, or SRP, is the S in the SOLID principles macronym. The short version of it is that a class should have only one reason to change. Specifically, the reason referred to here is tied to the application's requirements. Each class should only be required to change in response to a single change in the application's requirements. These requirements typically correspond to real world things. For example, let's say the application needs to display a report for quarterly sales performance. The system's requirements might be that the report be rendered in a browser, that it include all sales for a given quarter grouped by individual sales representative. Either of these requirements might change in the future. Perhaps the reports need to be emailed as PDFs. Perhaps they need to be calculated monthly instead of quarterly or rolled up by sales region. Ideally, the format of the report and the details of how it is calculated should be separate responsibilities of separate classes.

Why is SRP important? Back in episode 15, Maintain Legacy Code with New Code, I explain one reason. Every time you change a class, you risk breaking classes that work with it. When a class has multiple responsibilities, it will likely need to change more often. Also, it's likely these responsibilities will be tightly coupled together by the class, making it harder to change them independently and further increasing the inherent risk of changing them. Let's say you have to fix a bug in an application. Would you rather have to work in a single class that literally only does the thing that has the bug in it, or would you prefer to work in a 2000 line long class that does 10 different things, all of which are intermixed in the class's 50 methods and properties? I know which one I prefer.

Too often, developers new to SOLID will hear about SRP and misunderstand a bit what a responsibility really is. They might have a class they've carved out from the rest of the application. Something like CustomerManager. They'll happily explain how CustomerManager is only responsible for Customers in the application, and thus is following SRP. But upon reviewing the code, it's clear that CustomerManager is responsible for Customer validation, Customer persistence, and Customer formatting for various user interfaces. It's responsible for notifying Customers when they place orders and oh yeah, it also is responsible for Customers placing orders, as well as managing their orders and their payment providers and their account history. But don't worry - CustomerManager only has one responsibility: Customers!

Although I said responsibilities typically map to business requirements, they don't typically map to entities like customers or orders or policies. This is because these entities will often have a lot of rich behavior that can be isolated into separate responsibilities, often with cross-cutting concerns within the application. For instance, how a customer is validated will likely be very similar to how an order is validated. The same goes for persistence and UI formatting and a host of other activities. Each of these is a responsibility, and should be isolated in its own class.

Now, it might seem like breaking up big classes into small ones is going to result in a lot more code, but often just the opposite occurs. As you pull cross-cutting concerns like validation, persistence, logging, notifications, formatting, etc. into their own classes, you'll often find ways to standardize the codebase and reuse these classes. A common way to achieve this reuse is through the strategy pattern, which I discuss in episode episode 19. The Strategy pattern lets you move responsibilities out of a class by delegating to other classes, which are often defined as fields or properties on the original class. These fields or properties have their instances set by having them passed in through the constructor, in what's commonly called dependency injection. Dependency injection and the strategy design pattern go hand-in-hand, and are my favorite way to refactor to achieve SRP in classes that I find have too many responsibilities.

If you'd like to learn more about SRP and SOLID, check out my newly revised course, SOLID Principles for C# Developers, on Pluralsight. It's a complete update of my original SOLID Principles of Object Oriented Design course that has been in the top 100 for Pluralsight since it was published in 2010. The new one is shorter and streamlined, though it doesn't cover some topics the original course has. The new course also uses Visual Studio 2019 and has all of its course samples on GitHub. Check both of them out and let me know what you think.

Show Resources and Links

That’s it for this week. If you want to hear more from me, go to ardalis.com/tips to sign up for a free tip in your inbox every Wednesday. I'm also streaming programming topics on twitch.tv/ardalis most Fridays at noon Eastern Time. Thank you for subscribing to Weekly Dev Tips, and I'll see you next week with another great developer tip.