What is SOLID? Start implementing them to become a “solid” coder
By: Robert Pham Tran Tuan Linh
Tags: Java, Object Oriented, Solid

While you were learning Object-oriented programming (OOP), I am sure that you may have heard of the following terms and understand what they are:

Abstraction

Encapsulation

Inheritance

Polymorphism

Since most of you guys are familiar with the terms described above. Today, I am going to show you a few Design Principles in OOP, which are called SOLID.

Even though the SOLID acronym was introduced by Michael Feathers, the principles were first conceptualized by Robert C Martin in 2000.

SOLID principles consist of the following:

Single Responsibility Principle

Open/Closed Principle

Liskov Substitution Principle

Interface Segregation Principle

Dependency Inversion Principle

I will now explain to you briefly what these principles are and some of the examples where it can be used.

1- Single Responsibility Principle:

A class should hold only one responsibility. Furthermore, it should only have one reason to change.

Let us consider the following example:

public class Data Manager() { public void ImportDataFromDB(); public void ModifyData(); public void PrintData(); }

This class handle 3 responsibilities: ImportDataFromDB, ModifyData and PrintData. Therefore, if we were to change the database, or to change the printing format, etc. we will have to modify this class. When we keep making a lot of changes, the class will expand and become more prone to errors.

Therefore, according to the Single Responsibility Principle, we must separate this class into 3 separate classes. While having more classes to manage, the implementation inside each class will be much simpler and is less prone to errors.

2- Open/Closed Principle:

Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.

According to this principle, every time we want to add new functionality to the program, we should write a new class that extends the old class or associates with the old class. We should not modify the old class.

3- Liskov Substitution Principle:

In a program, objects of a subclass can replace the parent class without changing the correctness of the program.

It may not be easy to understand, but let me make it simpler for you with this example. Imagine we have a parent class named Car. The classes SportsCar, Minivan can inherit this class, and the program will run normally. However, let us say we have a ToyCar class, and it needs batteries to function. When this class inherits the Car class, it will cause an error because there is no battery in the Car class. It is a violation of the Liskov Substitution Principle.

4- Interface Segregation Principle:

Clients should not be forced to depend upon interfaces that they do not use.

In layman's terms, instead of using a large interface, we should split into many small interfaces, with many specific purposes.

This principle is quite easy to understand. Imagine we have 1 large interface, with about 100 methods. The implementation will be quite complex and difficult. In addition, it can also be redundant because a class does not need to use all 100 methods. When separating the interface into many smaller interfaces, only including relevant methods, the code implementation and management will be much easier.

5- Dependency Inversion Principle:

The principle can be described as:

High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.

As this principle is quite complicated, I will demonstrate it using a real-life example. We all know 2 types of lightbulb: incandescent bulb and LED bulb. If they both have the same screw base size, we can easily replace an incandescent bulb with an LED bulb and vice versa.

In this case, the main interface is the screw base, the implementation is the incandescent bulb and the LED bulb. We can easily swap between the two types of bulb because the screw base socket only cares about the interface and does not care about the implementation.

Similarly in coding, when applying Dependency Inverse, we only need to care about the interface. To connect to the database, we just need to call the Get, Save, etc. methods of the DBAccess interface. When replacing the database, we just simply need to change the implementation of this interface.