SOLID principles is an acronym that almost everyone who develops software has heard at least once. You can find thousands of content related to these SOLID principles when searched on the internet. Therefore, in this article, without going into details, I will summarize superficially, in a way that even a first-time reader can easily understand.
In summary, what is aimed with the SOLID principles is to ensure the reusability of the codes and to provide the project with the flexibility to preserve the readability of the project codes in case a new feature is to be added to the project.
SOLID abbreviation consists of the initials of 4 basic principles.
S – Single-Responsibility Principle (Each class/function should have only one task)
O – Open-Closed Principle
L – Liskov Substitution Principle
I – Interface Segregation Principle
D - Dependency Inversion Principle (implementing Dependency Injection)
Single-Responsibility Principle
A Class or function, method should have a single task and responsibility. It should not perform duties of other classes. To give an example on the basis of a function, if a function is obliged to add, this function should not be divided. With a more concrete example, if a function undertakes to update the password of a user, that function only needs to update the password information, it should not undertake the updating of other user data. If I give an example over the class. If we have a class named User, that class should only take responsibility for the user. For example, it should contain user-specific variables such as the user's name and surname and should contain functions accordingly. For example, updating the username would be a function of this class. If we need a function such as updating the address information of the user, or if we need variables such as the city, street, neighborhood where the user lives, creating a new class named Address and performing it from there, instead of doing such information and operations through the User class, will reduce the burden of too much responsibility on a single class. . This will make the classes cleaner and more understandable.
Open-Closed Principle
Indicates that an application, objects or entities are open for development but not for modification. For example, we have a class or interface called television. We have a function of this class called tvOpen(). One day we said that we are turning on this television and let's add the function of turning off the television. In such a case tvOpen(); If we have to edit the function, we are doing something against the Open-Closed principle. We should design the software architecture you will install in such a way that if we need an implementation such as turning off the television one day, tvOpen(); without touching the function, just tvClose(); We should be able to easily implement the TV off function to the project by adding a function called.
Liskov Principle
If the functions used or defined in an abstract class or interface cannot be used by overriding them by subclasses, we have written a code against the Liskov principle. There is an example of air conditioning that I like very much and that I always give. I am sure that after this example you will easily understand the main purpose of this principle. Let's say we have an interface called car. In this interface, I defined some functions that should be in a car. For example, one of these functions is air conditionerOpen(); be the function. Let's say I wanted to use this interface by implementing 2 classes named Ferrari and Lada. Since Ferrari has an air conditioner, I can easily implement this function in my Ferrari class, but when you think that Lada does not have an air conditioner in real life, although there should be a function called air conditioner in Lada class, air conditionerOpen(); The function will either return a NULL response or be a function that throws an Exception in the Lada class. This is a behavior against the Liskov principle.
Interface Segregation Principle
If the function defined in an interface is not used even in one of the classes to be implemented, divide that interface class into two or write a different interface. In short, a class should never contain an interface function that will never be used. For example, I gave the example of a car - air conditioner under the title Liskov. If the air conditioning feature is not available in every vehicle, we can create a new interface in the form of Luxury Vehicles. Of course, nowadays it is not a luxury for a vehicle to have air conditioning, but I wanted to give such an example to make the subject more understandable.
Dependency Inversion Principle
It is a principle that minimizes inter-class dependency. The implementation of this principle is called Dependency Injection. This principle recommends injecting the object of the superclass into the subclass, rather than linking the superclass directly to the subclass. To do this, the object of the superclass is used as a parameter in the subclass. If you're using Spring Boot, you've unwittingly done this over and over with the @Autowired notation. You can pass the superclass object as a parameter to another class without using notation, with the getter and setter methods or directly through the constructor classes. Actually the @Autowired notation does this too. Thus, we can use the implementations of the superclass in the class we want. This principle does "decoupling" between classes. In this way, we separate the classes by abstracting them from each other. In this way, we provide flexibility to the project by preventing code confusion that may arise as the project grows. There are thousands of content and examples on the internet about this subject. With a little search, you can find a lot of examples and reinforce this subject even more. In my opinion, it is a subject that must be known and mastered in the field of software development.