Understanding SOLID Principles: Single Responsibility
12 august 2020
SOLID principles are regarded as one the best practices in programming. We hear about them all the time. Unfortunately, it seems to me that these rules are often misunderstood. I’ll try to present them in the series of five articles, one article for each rule.
How has it all started?
The rules originated in the 80s of the twentieth century. As a whole they were collected by Robert C. Martin – a famous “Uncle Bob”, an American software engineer, instructor, and best-selling author. He is most recognized for developing numerous software design principles and for being the founder of influential Agile Manifesto. Uncle Bob, if You read it by any chance, I’d like to thank you for each sentence from your books, which were, and still are, very precious to me. I’m constantly discovering how deep they are. At the beginning they were not called SOLID at all, it was one of the friends of Uncle Bob who suggested a different order so that a famous mnemonic acronym appeared. And, starting from 2004 these rules has been functioning under the smart name SOLID. They appeared in the context of object-oriented programming but can be referred to as any blocks of the code. Thanks to them our software should:
- Be flexible to changes,
- Be easy to understand,
- Enable to create components used in many various systems.
Definition of Single-responsibility principle
This rule, although at first sight seems to be the simplest of them all, appears to be worst understood. Many times, either while talking to the software engineers or while working over the project, or even in the course of recruitment process, when I ask to describe a chosen rule I can hear about the SRP that “class shall be responsible for one thing”. I usually ask an auxiliary/rescue question like how many public methods a class should have then. Almost always I hear “one”. This is not true. It’s a circulating opinion. Even Uncle Bob admitted after some time that the name was not accurate. As an author I suggest that a class (module, code block, etc.) shall have only “one reason for changes”. How can it be understood? Let’s look at the example.
Breaking the rule
Let’s imagine a usual class with user data. There have been three roles of user-actors defined in the system:
- Human resources department – prepares work time reports,
- Payroll department – calculates salaries of employees,
- Administration department – introduces employees to the system and makes updates in case of any change.
Both payroll and human resources departments use the method that calculates the number of working hours – RegularHour(). Let’s assume that payroll department needs to change the method of calculating reported working hours in such a way that work during nights or weekends is double counted . Thanks to that it is possible to calculate the salary on the base of one rate. At the same time, HR department requires reported working hours to be calculated with a certain coefficient which will correct working time in such a way that a breakfast break or informal coffee breaks will be deducted from this reported working time. In the best version the method that calculates the number of working hours will contain a lot of conditions. In the worst version, while the second report is serviced, the changes introduced by the first one will be deleted.
Correcting the situation
The above example is not wrong because of several public methods, it is wrong because the reasons for changing EmployeeData class are overlapping. HR department or payroll department can demand changes that interact with each other. The easiest way to improve the code is to separate the class in such a way that functionality of each actor is contained in a separate class.
Now, if any department will demand changes to be made they should not influence functionalities that are used by other departments. However, 3 new classes have appeared in a code. Each of them can be potentially modified but by one actor only. It must be remembered that an actor can be a group of users but these will be users that make the same tasks in the system. Otherwise, it can be said that each class has one stakeholder.
It is possible that the change of each place in which the original version of a class was brought about will be troublesome and will require a huge amount of time. In such a situation it is worth to use the facade pattern.
Examples of other applications
The rule can be applied in reference to other structures of the code. And so, as an example, form user that collects contact data of an employee and client, despite the fact that it enables to introduce the same data it has different reasons for changes. It is worth mentioning, after the author of these rules, that another rule – DRY, is not violated when we have the code that looks the same but realizes a separate business tasks that have other reasons for changes.
About the software architecture in brief
Looking at SRP in a bit wider context allows to undertake proper designing methods. Let’s see how the application architecture that is built from API on a server and Angular application on Frontend, consequently maintained for all layers of application, could look like.
It is not difficult to imagine that in each component (class) there will occur sequences of conditions “if” that define specific behaviors for each actor. Such an architecture will cause problems with every change. Situation can be improved by using SRP in each layer.
Such an architecture will be surely more legible, easier to maintain and more resistant to changes. Additionally, such an application will be easier to scale or shape into microservices.
Using SRP does not necessarily mean that our class should have one public method and do one thing only. It should only has one reason for a change”. Thanks to it changes that will appear will not lead into situation when one change overlaps another. It is worth adding that the rule can be applied both in reference to the class and to the component, module or library. Thanks to SRP not only our classes will be better but also the whole architecture of our applications. I hope, my dear reader, that when we meet again you won’t tell me that the class shall do one thing only.