The SOLID principles acronym was introduced by Michael Feathers for five principles that were defined by Robert C. Martin in the early 2000s.
We should have a low coupling, high cohesion and strong encapsulation, which is something that the SOLID principles help us obtain. The idea is that, by applying those principles together, you are able to write better quality code that is robust. The system created becomes easy to maintain, to reuse and to extend over time. Basically, SOLID principles help software developers to achieve scalability and avoid that your code breaks every time you face a change.
S – Single-Responsibility Principle.
As you can see, this principle states that an object/class should only have one responsibility and that it should be completely encapsulated by the class. Here, when we talk about a responsibility, we mean a reason to change. This principle will lead to a stronger cohesion in the class and looser coupling between dependency classes, better readability, and code with lower complexity.
It is much more difficult to understand and edit a class when it has various responsibilities. So if we have more than one reason to change, the functionality will be split into two classes and each will handle its own responsibility.
We care about separating the functionalities because each responsibility is an access of change. When a class has more than a single responsibility, those responsibilities become coupled and this coupling can lead to a fragile code base that is difficult to refactor when your requirements emerge.
O – Open-Closed Principle.
Here, the idea is that an entity allows its behavior to be extended but never by modifying its source code. Any class (or whatever you write) should be written in such a way that it can be used as is. It can be extended if need be, but it can never be modified. You can consider this when you are writing your classes. Use the class in any way you need, but modifying its behavior comes by adding new code, never by modifying the old. The same principle can be applied to modules, packages, and libraries.
By applying the open-closed principle you will get a loose coupling, you will improve readability and, finally, you will be reducing the risk of breaking existing functionality.
L – Liskov Substitution Principle.
As its name says, Liskov's Substitution Principle was defined by Barbara Liskov. The idea here is that objects should be replaceable by instances of their subtypes without affecting the functioning of your system from a client’s point of view. Basically, instead of using the actual implementation, you should always be able to use a base class and get the result you were waiting for. Often when we want to represent an object, we model our classes based on their properties, but, instead of that, we should actually be putting more of our focus on the behaviors.
This principle basically confirms that our abstractions are correct and helps us to get code that is easily reusable and class hierarchies that are very easily understood.
What many say is that Liskov’s Substitution Principle has a very strong relation with the previous principle, the Open-Closed Principle. Robert C. Martin even says that “a violation of LSP is a latent violation of OCP.”
I – Interface Segregation Principle.
Here, it’s about how to write interfaces. So what is stated? Basically, once an interface is becoming too large/fat, we absolutely need to split it into small interfaces that are more specific. And the interface will be defined by the client that will use it, which means that the client of the interface will only know about the methods that are related to them.
Actually, if you add methods that shouldn’t be there, the classes implementing the interface will have to implement those methods as well. That is why the client shouldn’t be forced to depend on interfaces that they don’t use. ISP is intended to keep a system decoupled and thus easier to refactor, change, and deploy.
D – Dependency Inversion Principle.
The last of the SOLID principles, but not the least, this principle is primarily concerned with reducing dependencies amongst the code modules. Basically, the Dependency Inversion Principle will be of a great help when it comes to understanding how to correctly tie your system together.
If your implementation detail will depend on the higher-level abstractions, it will help you to get a system that is coupled correctly. Also, it will influence the encapsulation and cohesion of that system.