SOLID Principles with real-world examples

 

What are SOLID principles?

 

SOLID is a collection of five object-oriented design (OOD) principles introduced by Robert C. Martin. It should be followed when designing software. These principles layout guidelines for designing software while taking into consideration the project's long-term maintenance and expansion. These principles can assist in avoiding code smells, refactoring code, and adaptive software development and when you are programming, you can follow good practices in order to avoid issues to make your software easier to understand and also more robust and maintainable. Some of these good practices sometimes come in the form of principles and in object orientation, one of the most important principles is the solid principles, so let's look a little bit at what they are. 

 

SOLID stands for,

1.  Single Responsibility principle

2.  Open/closed principle

3.  Liskov’s substitution principle

4.  Interface Segregation principle

5.  Dependency Inversion principle


Let's start with a Single Responsibility. Single responsibility, which is defined by Uncle Bob is a class that should have only one reason to change, so it's also called high cohesion or when you try to have high cohesion in the class. The idea is to try to narrow down the behavior of class can have and motivations could rewrite it. For example, let's imagine a vehicle class. So, if you have a class vehicle where you have other responsibilities regarding the whole vehicle, for example including engine or tires and methods like get engine oil or get field level or get air pressure could end up with a class that is very big and it has a lot of responsibility into knowing what is specifically, does it mean to get the engine oil while you could split the class in multiple ones like engine tires, windshield, etc where you could just have specific methods in those classes that take the note more about the details on how to implement it and don't have too much responsibility.


Then we get to the open/closed principle. Basically, the open/closed principle says that a class should be open for extension but closed for modification. While the single responsibility principle refers to the purpose of a class like the internals, how many responsibilities do you have? This open/closed principle promotes more re-utilization, like moving the responsibility externally. So, when you split the responsibility of a class, you should do so in a way that the behavior can be extended or replaced instead of having to modify it. Going back to the vehicle example. Having different types of vehicles like cars or trucks vans or even tires that have a specific specialization that could be reused between them. So, in the case of the vehicle, for example, you would have to implement acceleration in all the classes. You could just have a vehicle class where you implement acceleration and then cars and trucks could have specializations on top of that.


Then we get to Liskov’s substitution principle. The definition is you should be able to change an instance using a sub-type and your code should still work. You may have seen some code where the interface of superclass has a method that the subclass implements with throw new unsupported exception. This violates Liskov’s substitution principle. The reason is that you should be able to change any class that depends on that interface to change the instance using your specific class implementation and it shouldn't affect the other class, but if you're throwing an unsupported exception then you might break the other code. Imagine for example having a vehicle class with a method called the shift to change the gears. Due to the nature, electric cars can shift, so if we extend the electric cars from vehicles, it would be a violation of Liskov’s substitution principle. We will be forced to implement a method that won't work either by throwing an exception or by doing nothing or something that doesn't make sense for a specific class, in this case, electric cars. So, we will see in the next principle how to fix this issue. 


We get to the next principle, the Interface Segregation Principle. Basically, what it means is that it's better to have too many interfaces that have too little. This is related to the previous principles that we have talked about, especially the Liskov’s and the single responsibility principles. If a class does too much because the interface is forcing it to. Replacing it with another class will be harder and you might end up having to implement methods and maybe it doesn't make too much sense for your case. For example, the electric cars or the vehicle example which was described above. Before going back to the example of vehicles and electric cars with the shift method, we can fix this by splitting the interface or the superclass into two types, or into subclasses. For example, combustion and electric cars. Combustion cars will have the shift method while electric cars won’t.


Finally, we get to the dependency inversion principle. The dependency inversion principle says one should depend on abstraction and not concrete instances. Again, related to all the rest of the principles that have already been discussed. Instead of having a specific car with the specific instances of tires, for example, you could have a generic interface implementing tires and all the type of tires and your specific vehicle have a dependency on those on that interface around that class, and this will basically make your code more flexible. This is about solid principles with real-world examples.

 

Whereas executing these principles can feel overpowering at to beginning. Frequently working with them and understanding the contrast between code that complies with the principles and code that does not will help to form good design processes simpler and more productive. 


 


Comments