(SOLID) S — Single Responsibility Principle. (Part 1)
DISCLAIMER:
While there are numerous explanations about SOLID principles available online, I’ve noticed that they often lack clarity. Therefore, it might be beneficial to dive into this series of articles thoroughly to gain a better understanding of these principles and learn how to enhance your codebase through these time-tested and straightforward techniques. By adhering to these practices, you can ultimately attain increased code flexibility and maintainability.
What are SOLID Principles?
It is an acronym for five design principles of Object Oriented Design (was presented by Rober C. Martin in 2000) that tend to make the codebase more understandable, flexible, and maintainable (wiki -https://en.wikipedia.org/wiki/SOLID).
As has been mentioned, it is an acronym where each letter has a meaning:
S — Single responsibility principle.
O — Open-Close principle.
L — Liskov substitution principle.
I — Interface segregation principle.
D — Dependency Inversion principle.
Explanations with examples
In this article, we are going to go over the first “S” principle (the rest will be explained in the following articles) where the following will be explained:
- Term for the statement itself, what is “S”.
- An example that violates this principle.
- One of the possible solutions to fix it.
Term for the statement itself, what is “S”.
According to the “Clean architecture” (wiki — link) this principle describes as:
The single-responsibility principle (SRP) is a computer programming principle that states that “A module should be responsible to one, and only one, actor.”
It could also be described as:
Each class should have only one responsibility. To put it differently, a class or module should have a single reason to change. It doesn’t mean that class or module should do a single thing but it should have a single cause to be changed.
The next reasonable question from this statement is: What could be considered as a “reason to change”? Is refactoring a change? Is a bug fix a change?
So from this point, I think that example will explain better than words.
An example that violates this principle.
class Employee {
fun calculatePay() {
//...
}
fun reportHours() {
//...
}
fun save() {
//...
}
}
In the given class, we have three different methods where:
- calculatePay()- hypothetically could be requested for change because a finance department probably needs to change something in the tax requirement.
- repotHours()- could probably be changed by HR as they need to add overtime.
- save()- from the technology department as DB technology has changed.
Thus we have 3 different departments (and 3 different reasons for change, not one) that possibly could request a change in the implementation, thus it clearly violates the SRP principle in a given implementation.
In order to get a clear idea of what exactly violation is here please take a look at the following section with a possible solution on how to divide the responsibilities to avoid violation.
One of the possible solutions to fix it.
Instead of keeping all these functions in the “Employee” class, it is better to pull these functionalities from there and create dedicated classes for each of the logic flows, like this:
class PayCalculator {
fun calculatePayFor(employee: Employee) {
//...
}
}
class WorkHourReporter {
fun reportHoursFor(employee: Employee) {
//...
}
}
class EmployeeDataSaver {
fun save(employee: Employee) {
//...
}
}
These classes take an “Employee” as an argument and do their job.
*Maybe the names of the classes or functions are not perfect, however, the idea is to show how to divide the responsibilities.
Now if the change came from no matter which department, this will only target a specific class. No problem in the “Employee” class and no problems in other classes, each of them is responsible for only one thing.
Conclusion:
So the idea should be pretty clear by now, but I would like to demonstrate one final analogy that I found really helpful. This comes from the “Butterfly Effect”, which means that minor changes like when a butterfly flaps its wings could cause a tornado a few weeks later somewhere else in the world. What we actually aim to do with SRP is to reduce or illuminate such kind of effect in our code.
It is a very classical example of it when developers make a change in one class and the next other class breaks or unit test breaks, so one (and usually main) of the reasons for such unexpected behaviour is that those classes violate this principle.
What we actually need is to change something and don’t worry that this change affects somewhere else. So then it became clear that in order to achieve that we need to “isolate specific responsibilities” or to put it differently — reasons to change.
Ok, this is what the Single Responsibility Principle is:)
What is next?
The next article about “O” — Open-Closed Principle under development:)
Useful links:
Thanks for taking the time to read through my article. If you enjoyed this article, please click on the clap icon a few times or share it on social media (or both). If you found something to be not quite right or have other information to add please reach out in the comments section below.