The Single Responsibility Principle Explained in Python.
High cohesion, low coupling and the hallmarks of quality software with SOLID.
In this post we will be looking at the first of the SOLID principles.
SOLID is an acronym for five software design principles that help make software robust and maintainable. Understanding and applying the five principle is the hallmark for good software engineering. Therefore it is crucial for any aspiring software engineer to be acquainted with them.
Before we dive into the principle two concepts must be explained in relation to software engineering. Namely cohesion and coupling.
Cohesion refers to the interconnection between functions in a class.
While coupling refers to the interdependence between classes.
The goal is to develop software with high cohesion and low coupling.
This means we want functions in a class to be performing tasks that are related to each other. Let's imagine the overall task of some software is to generate a report and sending it. Having all functions in one class is an example of low cohesion because the styling the report and sending it, are two different sub-tasks. Instead, we want the functions responsible for styling the report and inserting content to be part of one class and the functions responsible for sending it, part of another.
On the other hand, we want low coupling. This means classes should not depend too much on each other. So the class responsible for sending the report should not be too concerned with the format, style, or length of the report. It should be loosely coupled. If we change the functions that style the report or set the format, the function responsible to sending the report, should still work without errors.
You can imagine if coupling is high there are many dependencies which means if something in one class changes that change is propagated throughout other classes that depend on it and they would have to be changed as well. This would not only introduce bugs but also make code maintenance and future development a nightmare.
The single-responsibility principle helps us achieve this goal of high cohesion and low coupling.
Here's an image that illustrates the concept.
Now that that's out of the way, let's look through an example 😊
The S stands for the single-responsibility principle:
"There should never be more than one reason for a class to change."
In other words, every class should have only one responsibility.
Basically a class should only have a single purpose. If the functionality of the class needs to be changed there should be one reason to do so.
Let's imagine a car dealership selling 3 types of cars.
A user can:
- Request a car
- Test drive a car
- Buy a car
The code is as follows:
If the requested car is not available the user gets an error.
The price of the car is retrieved from the price list.
Finally, a user can test drive a car and also buy it.
It is only possible to buy a car by offering the full amount.
If you notice, the
Car class has multiple responsibilities - it needs to handle the car information and also the financials of purchasing a car. Thus it breaks the single responsibility principle.
Another way to see it is by using the definition of the principle above.
Namely, there should only be a single reason for a class to change.
Now let's imagine the car dealership wants to make two changes:
- Start offering customers to pay in installments instead of the full amount upfront
- Change the price of a car
There are now two reasons for the
Car class to change. There must only be a single reason for a class to change so the principle is violated.
We can easily fix this by making a separate class called
Financials and implementing the changes listed above:
If you notice now, each of the classes -
Financials only have a single reason to change.
It is now possible to pay in installments if at least 2/3 of the amount is paid upfront. This was updated in the
Financials class. Secondly, the price of a
BMW has changed from 100,000 to 200,000. This was updated in the
It might have been hard to see why it was a big problem to have both the car dealing and finance functionality in the same class. However, this becomes very apparent in medium to large organizational codebases which have many classes and dependencies.
Also, you may have noticed the code is now more loosely coupled than before as we have split it up into two classes. Also there is more cohesion as each of the individual classes now have a clear and defined task. The
Car class is only concerned with the car itself while the
Financials class only deals with financials.
Hope you enjoyed this post!
Enjoyed this article? Please consider subscribing using the form below😇
It is 100% free. You get an email every time I post (about once a week) + access to premium content in the future.