SOLID principles in programming

What makes Senior Software Engineers different from Junior Software Engineers

Mohith Marisetti
4 min readOct 9, 2021

What are SOLID principles exactly?

Image Credits: https://www.technologylogs.com/wp-content/uploads/2020/12/Solid_principle.jpg

SOLID is an acronym for 5 important principles i.e., Single Responsibility Principle, Open-Closed Principle, Liskov substitution principle, Interface segregation principle, Dependency Inversion principle.

Let us understand each principle in more detail.

Single Responsibility Principle:

This principle states that a class should only have 1 responsibility. It can also be said that a class should only have 1 reason to change. For example, let us consider an example of a car. A car has a lot of functionalities like opening/closing windows, turning on/off wiper, checking amount of fuel left in the tank, checking tyre pressure, steering, brakes, changing engine oil, etc.

This class has a lot of responsibilities to perform (i.e., a lot of reasons to change) which is a violation of the SRP (Single Responsibility Principle). A way to fix this is to have separate classes for each of the responsibilities.

Now, we have isolated the changes to its own class. Any change in these classes would not affect the Vehicle class.

Open-Closed Principle:

This principle states that “A class should be open for extension but closed for modification”. Let us consider a shop that accepts Credit cards & Debit cards from its customers to pay for the purchased products. The code to achieve that is as follows,

Let’s say in the future we got another method of payment i.e., NFC. To add this we create another class like

This is not sufficient to make the NFC payments work we also have to modify the existing Shop class as follows,

As we can notice if we want to extend the functionality we are having to modify existing code which is a violation of the Open-closed principle. To solve this we refactor the code as follows, first we create an interface with a pay method.

All the payment types (Credit card, Debit card, NFC) extend this interface.

With these classes in place we can now refactor the shop to use the Interface rather than actual implementation.

So, when the method purchase() is called, the caller can pass instance of either CreditCard, DebitCard or NFC. Also, this code is extensible without requiring any modification to the business logic i.e., RefactoredShop class.

Liskov Substitution Principle:

This principle states that the code should work even if an instance is replaced by a sub-type. Let us consider the earlier example of a Vehicle,

Let’s take an ElectricVehicle class now.

Few methods like checkAmountOfFuelLeftInTank() & changeEngineOil() from Vehicle class don’t apply for Electric vehicles. So, we have to throw an UnSupportedOperationException for such methods. This is a violation of the Liskov substitution principle as we cannot interchangeably use Vehicle & ElectricVehicle classes. To fix this issue lets see the next principle i.e., “Interface Segregation principle”

Interface Segregation Principle:

This principle states that it’s better to have more number of classes/interfaces than having very less. Taking the previous example the Vehicle class is forcing the ElectricVehicle class to throw UnSupportedOperationException for methods it doesn’t support. To fix this issue we can segregate 2 different classes CombustionVehicle & ElectricVehicle that extend from the Vehicle class which only contains methods common to both Combustion & Electric vehicle classes.

Every class will have its own custom methods and all the common methods would be available in the parent class.

Dependency Inversion Principle:

This principle states that rather than depending on a concrete implementation it's better to depend on abstract classes.

Pro Tip: There is a good saying that’s followed by Senior Software engineers i.e., “Always program to abstractions”. This means its always better to use Abstractions/Interfaces rather than direct implementations. It’s helpful because callers of a class method no longer have to worry about the underlying implementation.

Extending on the same example we used in the Open-closed principle. We have already followed the Dependency principle there. You ask how?

If you notice the argument to purchase method, we use interface rather than concrete implementation. This helped us to avoid duplicate code and to improve extensibility & maintainability. The call to this purchase method can be made using either CreditCard/DebitCard/NFC class. Either of these classes would work because they all are implementations of the IPaymentProcessor interface.

So, these are the 5 SOLID principles used by professional software engineers. If you are a Junior Software engineer aspiring to become a Senior software engineer, keep these 5 principles in mind while writing your code and you are on your way to becoming one. That being said, I thank you for reading it this far. Give a clap if you like this article and make sure to check out my other articles.

--

--