top of page
  • Writer's pictureJennifer Eve Vega

SOLID: Single Responsibility Principle

Updated: Aug 8, 2023

What does Single Responsibility Principle mean?

Every module/class/function should only have one responsibility.


This principle is probably the most ambiguous one for me. I do not claim to be an expert, but this is what I understand about Single Responsibility Principle.


Notice how we started from the bigger scale (module), going to the smallest unit (functions), because the Single Responsibility Principle is not just for our classes.


Functions should only do one thing and the name of our function should define what it's suppose to do. If it's doing something else outside of its "scope" or "responsibility," then it is better to take out that code and put it in a different function. That way, we can reuse our codes without worrying if it'll do something it's not suppose to do. For example, if its job is to compute the GPA of a student, then it shouldn't do something else like sorting the grades from highest to lowest.


A class' responsibility may not be implemented with only one function, there might be other related functions needed in order to let the class do what it's suppose to do. So here, we are not just limiting SRP to functions, but to the responsibility of a class as well.


If you're doing modular architecture, your module should be responsible for a specific flow. Example you have a Login module, wherein you have to implement multiple login options via social media, or magic link, or via email, all those options are part of the login module. And, if you require 2FA, that should also be part of the Login module, too. This responsibility cannot be implemented with just one class, you'll need other classes to create the whole Login module.


Single Responsibility Principle is not about having only one method in a class, we can also group related methods. Example, if you have an entity, User, then it is okay to group related methods about the User, example, putting all its CRUD methods in one protocol, and one class.

protocol UserProtocol {
	func saveUser(_ user: User)
	func getUser() -> User?
	func updateUser(_ user: User)
	func removeUser()
}

class User: UserProtocol { ... }

Story Time:

Few years back while I was still interviewing for a job, I mindlessly followed this principle separating almost every function and put it in its own protocol.

protocol GetUserProtocol {
    func getUser() -> User?
}
protocol SaveUserProtocol {
	func saveUser(_ user: User)
}
protocol UpdateUserProtocol {
	func updateUser(_ user: User)
}
protocol RemoveUserProtocol {
	func removeUser()
}

Literally created different protocols for each "responsibility." I ended up having lots of protocols, and then created a typealias protocol to combine them and make my class conform to it.

typealias UserProtocol = GetUserProtocol & SaveUserProtocol & UpdateUserProtocol & RemoveUserProtocol

class User: UserProtocol { ... }

I wanted to impress them that I know SOLID principles and I thought that's the right way to go. But as I said, I was mindlessly following it, making it a "law" that should not be broken. And when you blindly follow something, you won't be able to defend the reason why. They asked me why did I separate them, and I couldn't answer that it was "because of SRP." Now, I know that the reason I was not able to answer back then was because even I was not convinced that what I did was right.


Are we suppose to do what I did?

Well, in my opinion, we should group them together at first, and if a need arises that will force us to separate the protocol, then we do it. For now, if we don't need to, then let's group them together.


Doing something like what I did above is over engineering, I separated it because I thought we might need it in the future. I separated the protocols because I was thinking that in the future, maybe another class will simply need to get the User, and we don't need the other methods, which is a valid reasoning. But again, I was only creating a very simple (test) application at that time, and doing so, even though it didn't happen yet, made me over engineer the project.


How do we know that we need to separate it?

By checking if we will encounter the same "dependency issue" I mentioned in Interface Segregation Principle. When we are forced to implement some methods in a protocol that we actually don't need.


Accidental (or Incidental) Duplication

One thing I want to share is that it's okay to have accidental (or incidental) duplication in our codes. It means there can be codes that look really similar but the purpose is actually different, they represent different behaviors in the system.


For example, we have two screens that look almost the same, except the other one has one more/less section than the other. Each screen was used for FeatureA and FeatureB. DataModel for each feature is a bit different, too, but the data we show in the UI is almost the same.


Would it be better to have one screen and just use it for both features?

It might depend on your personal preferences. If it's me, I'll have separate screens no matter how closely these screens look alike, because each screen is responsible for its own feature, and if we just have one screen, it will now be responsible for two features. And I don't want to end up reusing the same screens over and over again especially if there are few more screens that look like it.


Maybe you'll ask me how about passing a Protocol which contains the data that we need to show to the screen? I mean, that'd be fine, too. Again, it's just my personal opinion to keep it separate just because it's used for different purposes (or feature), and I'm not against doing it this way.


My decision also boils down to our team using SwiftUI and we have our own component library, so the UI components are easily added to each screen, so it didn't matter to me if I create separate screens. If we're using UIKit, I might take a different route.


Conclusion:

As you can see, this principle is still quite vague (even for me). But I hope I was able to impart some of my knowledge about Single Responsibility Principle and the things I will do when faced with the same scenarios I have mentioned above.

Recent Posts

See All
bottom of page