L-One Systems Blog
How to design software that is easy to scale
SOLID-Principles for good software design
Digital solutions undergo lots of changes within their life cycle. To facilitate them, developers should follow the five SOLID Principles for good software design.
by Rana Al Huniess and Hamza Rabah, developers at L-One
A successful software product is constantly evolving. It is improved, receives new functions, additional interfaces and much more. However, if the software has not been set up well from the start, it becomes increasingly difficult to implement these changes.
If you want to avoid this, you should follow the five SOLID principles for good software design, introduced by the American software developer, IT consultant and writer Robert C. Martin in 2000.
Based on these principles, software is created that has the following advantages:
- Easy to maintain and test
- Easy to extend and reuse
- Easy to understand
The importance of the SOLID principles
"Good software systems start with clean code. Because, on the one hand, if the bricks aren't well made, the architecture of the building doesn't matter much.
On the other hand, you can make a considerable mess of well-made bricks. This is where SOLID principles come into play. "
Robert C. Martin
The acronym SOLID stands for:
- S - Single Responsibility Principle
- O - Open-Closed Principle
- L - Liskov substitution Principle
- I - Interface Segregation Principle
- D - Dependency Inversion Principle
We will present each principle individually to show how SOLID can help to develop better software that scales easily.
› Functions are self-contained modules of code that are intended to perform a specific task. Typically, this includes receiving and processing data and returning a result.
› Data structures are formats for retrieving, organizing, processing and storing data. They organize information for a specific purpose and make it easy for users to access and work with the information.
SOLID principles specify how functions and data structures should be organized into classes and how these classes should be connected. The use of the word "class" does not mean that these principles are applied only in object-oriented programming. In this case, the word "class" describes a particular combination or group of functions and data. The five principles should be applied to these groups.
1. The Single Responsibility Principle (SRP)
Robert C. Martin defines the first principle as follows:
"There should never be more than one reason to modify a class."
Robert C. Martin
However, this principle is usually described in more than one way. Other formulations include:
"A module should be responsible for one, and only one, actor. "
"Group things together that change for the same reasons. Separate things that change for different reasons."
L-One Systems developers are constantly improving their software design skills, such as applying SOLID principles. They spend 10 percent of their working time on continuing education.
Robert C. Martin explains, "Of all the SOLID principles, the Single Responsibility Principle (SRP) is probably the least understood. That's probably because the SRP has a particularly inappropriate name. It happens too quickly that developers hear the name and then assume it means that each class should only do one thing."
Unfortunately, this is a misconception. However, this is true for functions: A function should do one thing, and one thing only.
„Responsibilities“ are the primary axis for change
Martin defines a "responsibility" as a reason for change and concludes that a class/module should have only one reason to be changed. If more than one reason can be found for changing a class, then that class has more than one responsibility. This is sometimes hard to see because we are used to thinking of responsibilities in groups.
What is the problem if a class/module has more than one responsibility? Why is it important to split these responsibilities into multiple classes/modules?
The reason is that each responsibility acts as a primary axis for change. So when software requirements change (and they will), these changes are reflected in the responsibilities. If these responsibilities apply to multiple classes/modules, the latter have more than one reason to change.
As a result, implementing the changes will require a lot of effort.
Why is the single-responsibility principle important?
The following case illustrates the problems that occur when developers do not follow the single-responsibility principle.
Imagine that we have a "Person" class that is structured as shown in the figure below:
This design violates the first of the SOLID principles, the single responsibility principle (SRP)
The figure shows that the Person class has two responsibilities. The first is to report hours to HR, and the second provides payroll functions to Accounting. Both of these functions use the regularHours method, which returns the regular hours worked by employees, including break times.
By combining the source code for these two responsibilities into a single "Person" class, the developers have coupled the two actors, HR and Accounting.
How can this have a negative impact?
Imagine that the HR department would ask the developer to change the regular hours so that they no longer include break time. Then the developer would change the regularHours method to exclude break time.
However, this would affect the functionality of reportPayroll. This is because accounting still assumes that the regularHours method returns the time including the break time. The department's calculations are based on this assumption.
If the developer forgets to check which other methods use the regularHours method, this would result in incorrect accounting data without anyone noticing.
The coupling prevents the reuse of the functionalities within the Person class.
Also, it can cause the actions of HR to affect something that accounting depends on and vice versa. In other words, there is more than one reason to change a person.
Make digital solutions easily scalable.
The single-responsibility principle is one of the simplest principles, but also one of the most difficult to implement. We often connect responsibilities automatically. Finding and disconnecting these responsibilities is an essential part of software design.
If a function, module, or class has only one reason to change, it means that they conform to the single-responsibility principle. This makes changes easy to implement and the digital solution scales easily.
2. The Open-Closed Principle (OCP)
Robert C. Martin defines the second principle as follows:
"Software entities (classes, modules, functions, etc.) should be open to extension but closed to change."
Robert C. Martin
If a single change in an application leads to a cascade of changes to dependent modules - in other words, forces massive changes to the software - then the design is not ideal.
When the open-closed principle is applied well, changes are achieved by adding new code, not by changing old code that already works.
More flexibility, reusability and maintainability
The goal of the Open-Closed principle is to make a system extensible without making serious changes.
The application of this principle leads to the greatest advantages attributed to object-oriented technology: Flexibility, Reusability and Maintainability.
Classes/modules that conform to the open-closed principle have two primary characteristics:
1) They are open to extensions:
The behavior of the module can be extended. That is, as requirements change, the module can be extended to include new behaviors to meet those changes. That is, developers are able to change the behavior of the module.
2) They are closed to change:
Adding or extending the behavior of these modules does not change their source code, that is, the executable form of the modules is not changed.
Use abstraction, but correctly
It is clear that these two attributes conflict with each other, because the normal way to extend the behavior of a module is to modify its source code.
So the question is: how can we change the behavior of a module without adapting its source code?
The answer is: by using abstraction. Through the process of abstraction, a programmer hides all irrelevant data of an object to reduce complexity and increase efficiency.
There are some design patterns that can be used to apply the Open-Closed principle well, for example the following:
However, developers are responsible for applying abstraction only to those parts of the solution that change frequently. Resisting premature abstraction is as important as abstraction itself.
Use our expertise: Free of charge & without obligation
Get a professional assessment of your technical challenge from Lionel Born, CEO L-One.
He will advise you on how to successfully master your technical challenge.
You will receive tips on how to make the most of the potential of offshore developers and successfully collaborate with an IT service provider.