Single Responsibility Principle
In the last article I talked about Clean Architecture and SOLID Principles. Now it is time to start to talk about the first of five SOLID rules, the Single Responsibility Principle. “S” in SOLID stands for Single Responsibility Principle, often abbreviated and referred to as “SRP”. The SRP says that “Every software component should have one and only one responsibility”. When we say software component, if we are talking in the context of an object-oriented programming language, the first thing that comes to our mind is the class. But the term software component could also refer to a method or a function or even a module. So, component can be a class, a method or a module.
As you can see, I have introduced this article with the image of a "Swiss Army Knife". Why?
Because it's a metaphor, a Swiss Army knife is a combination of a number of useful tools, each with a distinct purpose like a mini scissor, screwdriver, and so on. Although a Swiss Army knife is a versatile and highly regarded tool, the rules change when it comes to software. If?we think of the Swiss Army Knife as a software component, it violates the Single Responsibility Principle, for the reason that it has multiple responsibilities.
Why it is so important in software field that each component should have one and?only one responsibility?
Because in place of the term responsibility we could put a new phrase “reason to change”. So now we could rewrite the principle, “Every software component should have one and only one reason to change”. But what is the problem if our class has more reasons to change?
If a software component has multiple reasons to change, then the frequency of changes it will increase. Every change to a software component opens the possibility of introducing bugs into the software.?So, as there are frequent changes to a software component the probability of introducing bugs increase. This would require more time and more effort to be spent on re-testing the software after those changes are made, because we need to make sure we catch all the bugs before we release the modified version of the software. More time and more effort mean more money. So, the differences between following and not following the SRP could be a considerable financial impact!
And this applies not only to the SRP, but to all other SOLID principles as well. So we should understand that the SOLID Principles are not some fancy good-to-have for our sw. These are important design principles, not following them it can result in significant software maintenance costs (Image 1).
Image 1: impact of the change in the software lifetime.
?Let's see a first example of Single Responsibility Principle violation, consider the class "Letter" shows in Image 2 (solution !) .?As can be seen in image 2 everything is inside the Letter: the contents (document), the knowledge of the logical transmission protocol (e.g. digital signature, charset encoding, ..) and the knowledge of dispatching mechanism ( e.g. unicast Vs. multicast).
?Instead of the single Letter class, Solution 2 shown in image 2 is much better and satisfies SRP.
Image 2: two solutions for modeling the Letter abstraction
Now we have divided the responsibilities into three separate classes (Letter, Envelope and Sender). There are three concepts: Letter (document), Envelope (deals with encoding, signature, ... and is also a package for letters) and Sender (knows the dispatching policy). Each class of the new design has a single reason to change. Single Responsibility Principle is now respected
Let's take another example: “a single class for a more different actors” (taken from “Clean Architecture” by Robert Martin).
Consider an Employee class from a payroll application. It has three methods: calculatePay(), reportHours(), and save() (Image 3).
This class violates the SRP because those three methods are responsible to three very different actors.
? The calculatePay() method is specified by the accounting department, which reports to the CFO.
? The reportHours() method is specified and used by the human resources Department, which reports to the HR Director.
? The save() method is specified by the database administrators who report to the CTO.
By putting the source code for these three methods into a single Employee class, the developers have coupled each of these actors to the others. This coupling can cause the actions of the CFO’s team to affect something that the HR team depends on.
Now the CFO wants to change the calculus of salary.
Unfortunately reportHours() and calculatePay() both use a private function regularHours() (Image 4). A programmer in order to implement the CFO require change regularHours(). When HR Director realizes that his data are wrong he gets very angry
Image 3: The Employee Class?
We’ve all seen things like this happen. These problems occur because we put code that different actors depend on into the same class. The SRP says to separate the code that different actors depend on.
Image 4: Shared algorithm
The most obvious way to solve the problem is to separate the data from the functions.
The three classes share access to EmployeeData, which is a simple data structure with no methods (Image 5). Each class holds only the source code necessary for its particular function. The three classes are not allowed to know about each other. Thus any accidental duplication is avoided.
Image 5: The three classes do not know about each other
Single Responsibility Principle is now respected. Again this time we have divided a big class in more classes in order to respect the SRP.
领英推荐
?Lastly let's see an example with the code snippet.
Let's consider a Client class which represents the client of a fitness center. This class was designed with too many responsibilities (Snippet 1). The Client class has a few attributes like clientID, clientName, clientAddress, contact number and it has a number of methods as well. It has save method which saves the client object to a database after serializing it. It also has a calculateSubscriptionFee method which calculates the subscription of a client. We could have three reasons to change, for this class.
First reason could be: Changes in the way the client attributes like id, name, adddress etc. are handled. Another reason could be a change the database , which would require changes to the save() method (for example we decide to replace MySql with NoSQL database like MongoDB, most of this code will need to change) .?Yet another reason could be changes in the subscription fee calculation, which would require changes to the calculateSubscriptionFee?method.
?So, the Client class has 3 reasons to change while Single Responsibility principle recommends only one.?
Snippet 1 : class Client with too many responsibility?So we'll first take the save() method out to a new RepositoryClient class (Snippet 2). The RepositoryClient class has a single responsibility of dealing with database operations and now the Client class has one less reason to change.
Snippet 2: class ClientRepository
?Then we'll take the subscribeFee() method out to a new SubscriptionFeeCalculation class (Snippet 3).
Finally the good thing is that each class has only one reason to change now (Snippet 4).
We started with one class, but we have ended up with three classes. So we have applied the single responsibility principle here. But a word of caution is that, do not keep creating a huge number of classes just like that. For instance, it is a bad idea to create separate classes to handle client id, separate class to handle client name etc.
These are very cohesive concepts, so they must be grouped into one class.
We will talk about cohesion and coupling in the next articles.
Snippet 3: SubscriptionFeeCalculator class
so the Client class becomes as shows in snippet 4
Snippet 4: Client class that respects SRP
Now that we have discussed the principle with some examples, I would like to conclude with one important recommendation: pay attention to the “manager classes”.
The “manager” classes, that is, classes whose name often ends with “er” or “or” [handler, manager, dispatcher, sender, etc). are the most frequent causes of SRP violation. In their worst manifestation, manager classes take over all the intelligence from the connected classes. That is, we have centralized control (controller) instead of distributed intelligence. However, it is very easy for programmers who think procedurally to fall into this trap and end up with a centralized controller that violates Single Responsibility Principle.
In the next episode I talk about two qualities of object-oriented software closely related to SRP: cohesion and coupling.
?I remind you my previous article:
thanks for reading my article, and I hope you have found the topic useful,
Feel free to leave any feedback
your feedback is appreciated
Stefano