Do you know how you can feel overwhelmed when you have too much on your plate? Well, your code can feel the same way. This is why it’s important to follow the Single Responsibility Principle, SRP, which is what the ‘s’ in SOLID stands for. Today we do a deep dive into the SRP and how you can ensure that your class or object only does one thing at a time. We take a look at some of the benefits of following the SRP, such as ease of testing and greater flexibility with making changes. When there is more than one responsibility designated to a class, it can quickly become confusing when you need to alter a piece of code. We also look into why you should try to keep classes as small as you can, how to know if a class or object has more than one responsibility, and why you should always be ready to refactor. If you want to clear the clutter, then this is the show for you!
Key Points From This Episode:
- An explanation of the comic that inspired this episode.
- Defining the Single Responsibility Principle, and clearing up some of the misconceptions.
- One way to ensure that you’re following best SRP practice: Rely on dependency injection.
- Making changes only in the file is another benefit of the SRP.
- Why not having class SRP can lead to confusion and messiness when you need to make a change.
- Keep your classes as small as possible so that unit testing is easier.
- How to uncover if your class is currently responsible for more than one thing.
- If your class has more than one responsibility, pull the implementation out.
Transcript for Episode 162. SOLID - Single Responsibility Principle
[0:00:01.9] MN: Hello and welcome to The Rabbit Hole, the definitive developer’s podcast. Live from the boogie down Bronx. I’m your host, Michael Nunez. Our co-host today.
[0:00:10.0] DA: Dave Anderson.
[0:00:12.2] MN: Today, we’ll be talking about the single responsibility principle, the S in solid.
[0:00:17.5] DA: And super too, right?
[0:00:19.0] MN: And super, yes, that letter’s also in super. It was a really wild convoluted way that I got to talk about solid I guess and what I will do is I’ll explain the comic that I read that introduce solid as a concept for me to want to have podcast episodes. Dave, are you ready?
[0:00:38.9] DA: Yeah, you were telling me that you saw this on Twitter and a friend of the show, Steve Nunez was explaining the joke.
[0:00:46.6] MN: Yeah, the first panel it’s like, it came to them with a message and it was like an alien on a space ship, looking down on humans with spears and the next text is, But they could not understand its alien language and then it’s a box with the alien, saying the following. “Microservice architecture is only necessary in a large complex dev environment. Most shops will be better off just using simple monoliths following solid principles and reap benefit from frequent small deployments.”
The next comic strip is the two humans on the ground, looking up at the alien, then one of them say, “F%$%ing what?” The other one, throws a spear at the alien and the two gentlemen sticks up their middle fingers at the alien sticks the middle finger back to the humans. Of course, us developers love microservices and don’t understand monoliths, why they’re useful.
[0:01:47.1] DA: I mean monoliths are the thing we talked about and I got to check my tats, what was it? This one’s really hard to reach. Number 63
[0:01:57.5] MN: Very long ago.
[0:01:59.5] DA: Yeah, a little while ago. This is a very involved message for these people like that you know, these cavemen don’t need their microservices, what are they doing? I kind of love this, this is a poorly drawn lines comic and they just like memed what the alien is going to say. I wonder if he intended for this. You know, this seems just like a perfect meme format, you can put anything in there. Solid principles, yeah, who knows?
[0:02:32.4] MN: Yeah, you know, if we all followed monoliths and add best practices using solid principles, we’ll all be enlightened engineers and I thought, “Hey, why not, why don’t we start with the first one, go down the line?”
[0:02:44.6] DA: Yeah, what is the first one again?
[0:02:49.1] MN: The first one is the single responsibility principle.
[0:02:51.0] DA: Right. Not super responsibility.
[0:02:57.8] MN: Yeah, I know. When your write your piece of code, you ensure that the class or the object that you have does one thing at a time.
[0:03:04.0] DA: Wait, I thought it was like, I’m not going to have as many responsibilities like I won’t’ need to walk the dog because I just need to record the podcast and maybe will have more free time like –
[0:03:14.5] MN: No, if you need to walk your dog and listen to the podcast, you should be able to do those two things, yes. Your class is an object should be responsible of one thing.
[0:03:25.4] DA: Okay. That sounds reasonable, yeah. But like you know, sometimes my one thing is like to send the email and when I send the email, getting all the data and I’m rendering the template and I’m you know, doing math.
[0:03:44.9] MN: No, that’s a lot going on there.
[0:03:48.2] DA: I mean, I’m just sending the emails, one responsibility.
[0:03:52.4] MN: Right, the idea of sending, the send email action should be responsible of collecting that data. One way you can ensure that you're following the best practices of the single responsibility principle. I don’t know if the kids call it SRP but I might just do that from now on, SRP.
[0:04:12.4] DA: Save all those syllables.
[0:04:14.9] MN: Save all the syllables, yes. You can actually depend on dependency injection so that your class isn’t having to pull all that information when it’s going to send an email in your example, but also be given all those objects that it can then send email if that makes sense. If you have to go and fetch all this information, you know, render anything or pull some information from state, the idea is that you want that to be done outside of the class that would be responsible to send email.
[0:04:52.8] DA: The thing that sends the email shouldn’t know necessarily the password for the database and the SMTP connection details and the templating engine, all these things?
[0:05:06.9] MN: No, that sounds like a lot of responsibilities here. By doing that, the implementation of what your class is trying to do is clear, right? If you were to open this send email class for example, you would know that that is responsible for sending emails. It doesn’t have any of the other funky things that are necessary for it to send an email or information outside of sending the email. You can then just use to send email, implementation to send emails.
[0:05:41.1] DA: Right, it would be clear when you’re reading it. Clean and clear, we’ve talked about in the past like clean code. You might just read it like - There might be a collaborating class that asks for things from other systems like it’s going to ask your database connector to get all the data and it’s going to ask the templated to do the stuff with the data to do the thing and then it will send the email with the email sender.
[0:06:16.9] MN: Right, I think another benefit of the SRP with that is that if you need it to change something that dealt with sending of the emails, you would only do it in that particular file. Let’s say for example if your send email class was also responsible of the templating aspect, and if you had a template class, you would have to change it in those two places.
Now, your send email class has a lot of information, has more than one responsibility because it’s either sending emails or it’s also templating the emails before sending it.
[0:06:55.3] DA: Right, if I go change to the email logic, I don’t have to worry about breaking the way the template works or the way the database processes things?
[0:07:04.5] MN: Right. I think that’s one of the things that ends up happening when your class does not have a single responsibility, it gets really confusing when you do need to make changes unintended behaviors may happen because a change needs to happen in one of your classes and if it’s a couple, it would be very difficult to untangle the mess.
[0:07:28.9] DA: Right, I imagine too that if you are trying to write a test for something that’s all tangled up, it’s going to be a little more complicated. You're going to have more set up, you’re going to have to have, you’re trying to test the email that you’re going to have the database stood up. Be like, “I got the things on the database. I’m going to need to have the template thing working and what not. I’m going to have to have the email and test server all set up,” and then at the end of that like that means I have to have all of those things set up and then if any one of those things in the middle is broken, then the thing at the end is not going to work like my test will fail for unrelated reasons then the email could be broken.
[0:08:22.3] MN: Right like you just mentioned all of that set up, boiler plate, mocking and stopping that needs to happen just to test an implementation of the send email aspect of the code will be really, really hard to test. Having to deal with that then you have to if you are writing tests like that then you are maybe less likely to want to write tests and then that leads to a whole other issue of decayed code because you choose not to write tests because they are just so difficult because their class has or is coupled to have all of these logic.
[0:08:54.5] DA: Right or talking about like past episodes like death by a thousand ifs.
[0:09:02.3] MN: Oof .Oh man yeah, death by a thousand ifs.
[0:09:05.9] DA: Episode 58, with Jacob O’Donnell, friend of the show, way back.
[0:09:09.9] MN: Yeah, friend of show. Way back. Deep in the – Tats kind of hard to find.
[0:09:16.1] DA: Yeah it is like base of my arm or something or my armpits.
[0:09:21.2] MN: So, right, if you’re following the single responsibility principle, the SRP, your classes will be a lot smaller and down to that one thing that needs to be responsible, then you can definitely unit test a lot better, know exactly, pinpoint the thing that you need tested. You don’t have to worry about that convoluted test set up and mocking and boiler plate. That would be necessary to run test on something that is highly coupled. So always makes sure to keep your classes as small as possible so that you can unit test a lot better.
[0:10:00.7] DA: How do I know if I am doing more than one thing?
[0:10:03.4] MN: Yeah, I just think you would have to check to see the things that it is responsible for. If you look at your class and know that it may be able to do all sorts of things like for example if it’s – I know in like different blog posts they used like shapes for example. So, if you know that your shape object is responsible for both like getting the area of a shape and then being able to print that shape out somewhere, then it may be responsible for too many things.
So, you have to break it up in a way that would be a little bit more clear in what the shape object is doing and when you’re going to say print out shapes or find the area of a shape and calculate mass or whatever that you may have like a shape calculator class that can take shapes and calculate certain things rather than having it in the shape object that has all of the knowledge of the shapes and all of the methods and stuff like that.
[0:11:05.8] DA: Right, I guess if you are seeing like a lot of parameters going into that shape object like to initialize it or on a method on that parameter or a function, then that could be an indication that it is doing too much as well? Maybe there is some kind of way to refactor another class that it depends on those sort of things or is injected in.
[0:11:40.1] MN: Right and I think yeah, if you sit down and look at the class and explain what it does, you may just even rubber duck through the idea that, “Hey, this is doing more than one thing. How can we extract this piece of code?”
[0:11:56.4] DA: Right, are you able to have a really short name or just describe? Yeah, if you can name it and not hide anything that it is doing then that’s probably a good indication. Because if my email method was just called send email and it did all the other stuff, like getting the database and doing the calculation on all of my shapes and then sending the email about all of my cool shapes from a template, then you know I am hiding the fact that it’s got some other things going on.
[0:12:34.9] MN: Right. I mean we all know that naming things is like possibly the hardest thing that we do as programmers but if you were to actually name your method Dave like, get information from database and pull template and grab shapes and send email then you would know like, “Okay, hold on a second.”
[0:12:55.1] DA: I mean it sounds good to me.
[0:13:00.1] MN: But yeah, I think keeping your class as small as possible and be descriptive in your names will definitely allude to whether you are following the SRP, single responsibility principle, and when you are not always be ready to refactor. I think when you are able to test drive.
[0:13:17.1] DA: That is ABR, always be refactoring.
[0:13:21.7] MN: Always be refactoring, yes.
[0:13:24.3] DA: Or I guess ready to refactoring is more productive, so yeah.
[0:13:28.2] MN: Yeah, I mean it is being proactive to be ready to refactor but always be refactoring when necessary and if you find that your class is now starting to look like it’s being responsible for two different things then you know, pull that piece implementation out into its own class so that it can be responsible for one thing and one thing only. All of your classes should have one job. Like you only had one job. You should be able to say that to all of your classes.
[0:13:58.5] DA: That’s true. Yeah as long as you have the test then you got a path to clean it up and break it down. I think we should do a podcast episode on refactoring steps because it is kind of cool how you can break it down one step at a time and even if it is something that’s like absolutely intense like the example of we gave if it’s like getting send an email and getting the shapes and doing the math on the area or whatever then, you can still break it down but you got to be methodical.
[0:14:31.9] MN: Hey, that will be next down in the future in The Rabbit Hole we would talk about those refactoring steps. I do think that when you’re working in a code base that is heavily reliant on your classes being having one single thing that it needs to do. It’s just more clearer for you to get whatever implementation you need out the door because you know that one change that you are going to make for this one class is not going to affect four other things because everything has been separated and everything had its own responsible thing that it needs to do without being really coupled.
[0:15:09.5] DA: Yeah what a relief.
[0:15:12.1] MN: So, Dave, you may have to walk your dog and –
[0:15:15.3] DA: Hey, I think I do actually.
[0:15:17.9] MN: And listen to a podcast at the same time. You should be able to do that. But when you are writing your code to just keep it short, keep it simple, single responsibility principle, SRP, get yours, don’t worry about it.
[0:15:31.9] DA: Be ready. To refactor.
[0:15:35.3] MN: Always be refactoring.
[END OF INTERVIEW]
[0:15:38.3] MN: Follow us now on Twitter @radiofreerabbit so we can keep the conversation going. Like what you hear? Give us a five-star review and help developers like you find their way into The Rabbit Hole and never miss an episode, subscribe now however you listen to your favorite podcast. On behalf of our producer extraordinaire, William Jeffries and my amazing co-host, Dave Anderson and me, your host, Michael Nunez, thanks for listening to The Rabbit Hole.
Links and Resources:
Our seasoned cross-functional agilists work with you to develop technology, ship product and deliver value. We leverage our diverse expertise across industries and throughout the organizational growth cycle to your benefit. We bring best practices, emergent practices and creative solutions to your problems.