SOLID Principles Java
In Java, SOLID principles are an object-oriented approach that are applied to software structure design. It is conceptualized by Robert C. Martin (also known as Uncle Bob). These five principles have changed the world of object-oriented programming, and also changed the way of writing software. It also ensures that the software is modular, easy to understand, debug, and refactor. In this section, we will discuss SOLID principles in Java with proper example.
The word SOLID acronym for:
- Single Responsibility Principle (SRP)
- Open-Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
Let’s explain the principles one by one in detail.
Single Responsibility Principle
The single responsibility principle states that every Java class must perform a single functionality. Implementation of multiple functionalities in a single class mashup the code and if any modification is required may affect the whole class. It precise the code and the code can be easily maintained. Let’s understand the single responsibility principle through an example.
Suppose, Student is a class having three methods namely printDetails(), calculatePercentage(), and addStudent(). Hence, the Student class has three responsibilities to print the details of students, calculate percentages, and database. By using the single responsibility principle, we can separate these functionalities into three separate classes to fulfill the goal of the principle.
Student.java
The above code snippet violates the single responsibility principle. To achieve the goal of the principle, we should implement a separate class that performs a single functionality only.
Student.java
PrintStudentDetails.java
Percentage.java
Hence, we have achieved the goal of the single responsibility principle by separating the functionality into three separate classes.
Open-Closed Principle
The application or module entities the methods, functions, variables, etc. The open-closed principle states that according to new requirements the module should be open for extension but closed for modification. The extension allows us to implement new functionality to the module. Let’s understand the principle through an example.
Suppose, VehicleInfo is a class and it has the method vehicleNumber() that returns the vehicle number.
VehicleInfo.java
If we want to add another subclass named Truck, simply, we add one more if statement that violates the open-closed principle. The only way to add the subclass and achieve the goal of principle by overriding the vehicleNumber() method, as we have shown below.
VehicleInfo.java
Similarly, we can add more vehicles by making another subclass extending from the vehicle class. the approach would not affect the existing application.
Liskov Substitution Principle
The Liskov Substitution Principle (LSP) was introduced by Barbara Liskov. It applies to inheritance in such a way that the derived classes must be completely substitutable for their base classes. In other words, if class A is a subtype of class B, then we should be able to replace B with A without interrupting the behavior of the program.
It extends the open-close principle and also focuses on the behavior of a superclass and its subtypes. We should design the classes to preserve the property unless we have a strong reason to do otherwise. Let’s understand the principle through an example.
Student.java
The above classes violated the Liskov substitution principle because the StudentBMI class has extra constraints i.e. height and weight that must be the same. Therefore, the Student class (base class) cannot be replaced by StudentBMI class (derived class).
Hence, substituting the class Student with StudentBMI class may result in unexpected behavior.
Interface Segregation Principle
The principle states that the larger interfaces split into smaller ones. Because the implementation classes use only the methods that are required. We should not force the client to use the methods that they do not want to use.
The goal of the interface segregation principle is similar to the single responsibility principle. Let’s understand the principle through an example.
Suppose, we have created an interface named Conversion having three methods intToDouble(), intToChar(), and charToString().
The above interface has three methods. If we want to use only a method intToChar(), we have no choice to implement the single method. To overcome the problem, the principle allows us to split the interface into three separate ones.
Now we can use only the method that is required. Suppose, we want to convert the integer to double and character to string then, we will use only the methods intToDouble() and charToString().
Dependency Inversion Principle
The principle states that we must use abstraction (abstract classes and interfaces) instead of concrete implementations. High-level modules should not depend on the low-level module but both should depend on the abstraction. Because the abstraction does not depend on detail but the detail depends on abstraction. It decouples the software. Let’s understand the principle through an example.
It is worth, if we have not keyboard and mouse to work on Windows. To solve this problem, we create a constructor of the class and add the instances of the keyboard and monitor. After adding the instances, the class looks like the following:
Now we can work on the Windows machine with the help of a keyboard and mouse. But we still face the problem. Because we have tightly coupled the three classes together by using the new keyword. It is hard o test the class windows machine.
To make the code loosely coupled, we decouple the WindowsMachine from the keyboard by using the Keyboard interface and this keyword.
Keyboard.java
WindowsMachine.java
In the above code, we have used the dependency injection to add the keyboard dependency in the WindowsMachine class. Therefore, we have decoupled the classes.
Why should we use SOLID principles?
- It reduces the dependencies so that a block of code can be changed without affecting the other code blocks.
- The principles intended to make design easier, understandable.
- By using the principles, the system is maintainable, testable, scalable, and reusable.
- It avoids the bad design of the software.
Next time when you design software, keeps these five principles in mind. By applying these principles, the code will be much more clear, testable, and expendable.