This is the 8th article on the System Design and Software Architecture series. In this article, we are discussing the sub-topic of the design principle, SRP: The Single Responsibility Principle.
Previous articles
- Introduction to System Design and Architecture
- Values of system software
- Programming Paradigm
- Object-Oriented Programming
- Structured programming
- Functional programming
What is The Single Responsibility Principle
The Single responsibility principle is the core software engineering principle that determines how we should modulate the code in object-oriented programming.
The argument for the single warranty principle is relatively simple: it simplifies the execution of your software and prevents unexpected side effects of future changes.
Requirements are changing in the software development process. Each requirement changes at least one class. The more responsibilities you have in your class, the more you want to change them.
As soon as one responsibility in your class changes, you will change it. If it has only one responsibility, it is more likely that you will obviously have to change it.
The principle of individual responsibility is closely related to the concepts of connection and coexistence. Coupling refers to how different aspects of an application have the ability to link together and how closely the contents of a class or package can be linked. Since the class is a single unit that should need to use or not use at all (static methods and data discounts at the moment) all the contents of a single class are tightly bound. When other classes use a class and that class changes, the dependent classes should need to check to ensure that they are working correctly with the new behavior of the class. If there is poor coexistence in a class, some parts of it may change and only certain classes sometimes can use and others may remain the same.
However, all dependent classes in the class should have to re-examine as a result of the change. Then it is increasing the total surface area of the application affected by the change. Instead, if the class is divided into several upper correlational classes, each of them will use less of the other elements of the system. So modifying any of them will have less effect on the whole system.
Benefits of the single responsibility principle
The argument for the single responsibility principle is relatively simple: it simplifies the execution of your software and prevents unexpected side effects of future changes.
Frequency and Effects of Changes
We all know that needs change over time. They also change the responsibility of at least one class each. The more responsibilities you have in your class, the more you want to change them. If your class executes different responsibilities, they are no longer independent of each other.
As soon as one of your class responsibilities changes, you must change it. If it has only one responsibility, it is more likely that you will obviously have to change it.
While it may not seem like a big deal, it does affect all classes or components that depend on the changed class. Depending on your change, you may need to update dependencies or change dependency classes again. They use only one of the other responsibilities enforced by your class, but you must update them anyway.
Easier to Understand
The single liability principle offers another significant benefit. Classes with only one responsibility, software components, and microservices are easier to explain, understand. And implement than those that provide a solution to everything. This reduces the number of errors, speeds up your development, and simplifies your life as a software developer.
However, it is important to be sure not to simplify your code too much. Some developers take the single responsibility principle to the extreme by creating classes with one task. Then, when they want to write some real code, they have to inject a lot of dependencies, which makes the code very unreadable and confusing.
Therefore, the single responsibility principle is an important rule to make your code more understandable, but do not use it as your programming bible. Use common sense when developing code. It does not make sense to have multiple classes containing only one function.
How Can This Principle Be Misleading?
The strategy for implementing SRP in our software is to know the responsibilities of each class.
However, every developer has their own vision of the class objective, which makes things confusing. Since we do not have strict instructions on how to implement this principle, we are left with our own interpretations of what constitutes liability.
This means that perhaps as creators of our app we can determine if something is within the scope of the class.
When writing a class according to the SRP principle, we need to think about the problem domain, business needs, and application architecture. It is very subjective, making it difficult to implement this principle. It will not as simple as the example we have in this tutorial.
This takes us to the next point.
Cohesion
- Following the SRP principle, our classes will adapt to one activity. Their methods and data are concerned with one clear purpose. This means high coexistence as well as robustness combined to reduce errors.
- Coexistence is essential when designing software based on the SRP principle, as it helps to find individual responsibilities for our classes. This concept helps us find classes with more than one responsibility.
- Let us go back to our TextManipulatorclass methods:
- If we do not think about coexistence and we do not have a clear definition of what this class is responsible for, then we can say that text writing and updating are two different and separate jobs. With this in mind, we can conclude that these should need two separate classes: WriteText and UpdateText.
- The reality is that we will get two classes that are tightly bound and loosely integrated, which should almost always need to use together. All three of these methods can perform different operations. But they essentially serve one purpose that is text manipulation. The main thing is not to overestimate.
- LCOM is one of the tools that help to achieve a high level of coexistence in methods. Essentially, LCOM measures the relationship between class components and their relationship to each other.
Examples
Here are some examples of responsibilities that have to separate:
- Relentless effort
- Validation
- Notification
- Handle error
- Login
- Class selection / acceleration
- Modelling
- Analysis
- Mapping
Types of Responsibilities
Instead of defining liability in abstract terms, it sometimes can wiser to list the actual types of liability. Here are some examples (they are derived from Adam Worsky’s object classification in applications, distilled in his thought-provoking post on Scala dependency injection):
Business logic
For example, we can see extracting a phone number from a text, converting an XML document to JSON, or classifying a money transaction as fraud. At the class level and above, business logic is responsible for knowing (or consolidating) business operations: for example, a class that knows how to convert XML documents to JSON or a fraudulent transaction detection service.
External integration
At the very least, this could sometimes an integration of modules within the application. Also, that is, queuing a message would be processed by another subsystem. Then, there are integrations with systems such as system timekeeping or testing. Finally, there are integrations with external systems such as RPC calls for database transactions. Read or write to a distributed queue such as Kafka, or other services.
Depending on the class, module, and service level, the responsibility for external integration lies in knowing how to integrate the external partition (or integrating integration): for example, a class that knows how to read system time (or exactly what java. time), or A service that speaks with an external API.
Data
Data is something like a person’s profile on a website such as a JSON document, a message. Embedding a piece of data can only may the responsibility of a class (object) but not a system, module, or service. The specific type of data configuration: A collection of parameters for another system, class, or system.
Control the flow
Control flow, activation, or part of the data flow of an application. An example of this responsibility is a way of calling out components that have different responsibilities to each other:
Conclusion
Thanks for reading the article SRP: The Single Responsibility Principle as an essential component in System design and architecture.
My articles on medium