S - Single Responsibilities Principle
Single Responsibility is a software design principle introduced by Robert Martin. As the name suggests, the pieces that come together are expected to serve the same purpose. Parts that do not serve the same purpose must be separated by dividing and combined in another module if they are to be used together.
The root of the SRP:
The main term that inspired the SRP is Cohesion, Cohesion was introduced by Tom DeMarco and Meilir Page-Jones for the first time. After that Robert Martin has inspired by cohesion and introduced the SRP for the first time in his articles.
There are two keywords that would be useful to talk about before we diving into SRP, these are Cohesion, Coupling.
Cohesion: Cohesion is describing the harmony within the elements that are located same class or module.
Like we saw above we preferred to separate each different shape no matter which color is it in one module because of the harmony they have with each other instead of separating them by colors and shapes. And we assumed the main module needs each module so we combined those modules in the main module.
Coupling: Coupling is describing the connections between classes. If you noticed that we separated all same typed shapes in one module and we put them into one main module so we made coupling there.
There are two more terms related to the above ones that would be fine to know, these are tight coupling and loose coupling.
Tight Coupling: It describes the case of that the classes which connected tightly with each other. When we need to add or delete a new element, we must edit the main class too. This type of coupling will make bulky your code, it will be increased your development time and will be made impossible the use that code in different parts of the project.
As we saw in the code block above when we want to add a new planet type we must edit the switch code in Universe class too.
Ardalis has a quote that I like much, that is “New is glue.”. Every time we get an instance of our classes in different classes (look at SRP — Tight Coupling — Universe.cs @ 11,15,19. lines) we couple these two classes with each other.
Loose Coupling: It describes the case of that the classes which connected loosely with each other and are manageable.When we need to add/ delete an element we can do it only by deleting or adding the class for the new feature. We don’t need need to edit the Universe class anymore.
Now that we’ve learned a lot of cool terms, I guess we can move on to the main topic..
I want to start with one of the most known sentences of Robert martin;
“A class should have only one reason to change,”
Each of your modules should have only one responsibility, if it has only one responsibility so that module can only be changed based on that responsibility.
How can we explain that ‘responsibility’ term?
The responsibility of our class is directly proportional to the possible reasons for changing that class. If we need to change this class for different reasons, it shows us that the class is violating the SRP because it has more than one reason to change.
So does this single responsibility principle mean that each class has only one method?
At this point where we get confused, the backbone of SRP, the concept of ‘cohesion’ by DeMarco and Page-Jones comes into help. According to the cohesion principle, the elements in our class can grow as long as they are compatible, and where they are incompatible, these elements should be removed from this class and must be separated into another module. This also means; Although our class has only one responsibility, it can have more than one method.
Maybe we can explain SRP best with the example of the cook,
The tasks of a cook are not limited to just cooking the food, it is also the task of a cook to select the ingredients, preparing the ingredients, calculate the ingredients count according to the number of people, and design the plate after the meal is cooked. As you can see, the cook has more than one task, but they are all compatible with each other. But if we assign this cook the task of washing dishes, we will be violating the SRP. In other words, we assign the cook a task that does not have his own job description and is not in harmony with his current tasks, which reduces the performance of the cook and may even lead to his resignation. If we do not ensure compatibility in our code, the development time of our modules and the rate of encountering errors will increase.
If a change in module A affects module B, it means that there is an element in module A that breaks the harmony and this element should be transferred to module B.
We’ve talked too much let’s practice with a few code blocks.
First of all, let’s create a very simple bad design that we can be faced all the time:
As you can see, our object instances, logging, emailing task etc. are in this class. So this class has more than one reason to be changed. This means that our class is violating the SRP.
So how can we fix this situation,
As you can see, we’ve removed our logging, mailing, working and completion flows from our main class with an abstraction. Our main class doesn’t know how to do these operations anymore, it just uses them. We loaded these responsibilities into our KeeperTasks class because these tasks have harmony with the KeeperTask class.
As you can see in our current code, there is a tight coupling situation (see line 14, 21, 24. lines), and we can turn this into a loose coupling with an interface as we learned in the tight coupling section.
Thus, if there is a change in our completion method flow or work method flow, our Main class will not know and this change will not cause any change in the main class. And as it seems, there is more than one method in our KeeperTask class because these methods have harmony with each other and the class.
Let’s say we were asked to write a method called ‘identify yourself’, if we write it to our KeeperTask class, we will break the in-class harmony. When we think about the in-class harmony, it is obvious that the Keeper class should be the place where this new method belongs. If we are asked to write a method that is not compatible with both, we will have to write a class with a new area of responsibility based on this new method.
Summary:
Cohesion: “The degree of functional relatedness of processing elements within a single module.” [Structured Design -Edward Yourdon]
Coupling: “Coupling as an abstract concept — the degree of interdependence between modules — may be operationalized as the probability that in coding, debugging, or modifying one module, a programmer will have to take into account something about another modules.” [Structured Design -Edward Yourdon]
Tight Coupling: “If you want to change the skin, you would also HAVE TO change the design of your body as well because the two are joined together — they are tightly coupled.” [BenKoshy] [1]
Loose Coupling: “If you change your shirt, then you are not forced to change your body — when you can do that, then you have loose coupling. When you can’t do that, then you have tight coupling. [BenKoshy] [1]
Single Responsibility: “A class should have only one reason to change” [R. Martin aka Uncle Bob]