Solid Principles in JAVA
Tags: java
Published: 2022-07-12 04:49:35



`SOLID` is Object oriented design concept relevant for software development.

Solid is an acronym for

 **S**ingle Responsibility Principle,
 **O**pen-Closed Principle, 
**L**iskov Substitution Principle,
 **I**nterface Segrigation Principle and
 **D**ependency Inversion Priciple 

## Single Responsibility Principle
A class should be doing only one thing and one thing only i.e. each class should be responsible for single functionality of the system.

Example that does not follow SRP:

```java
class Vehicle{
    public void calculateValue(){...}
    public void printDetails(){...}
    public void addVehicleInDb(){...}
}
```
class `Vehicle` has three separate works that it is doing. According to SRP it should be seggregated into different classes with single responsibility.

## Open-closed principle
A class should be open for extension but closed for modification

Consider the below method of class ` VehicleCalculations`

```java
public class VehicleCalculations {
    public double calculateValue(Vehicle v) {
        if (v instanceof Car) {
            return v.getValue() * 0.8;
        if (v instanceof Bike) {
            return v.getValue() * 0.5;

    }
}
```
Now if we want to add another subclass called `Truck` we will have to modify above method by adding another if statement, which goes against the Open-Closed Principle.
A better approach would be for the subclass `Car` and `Truck` to override `calculateValue()` method.

```java
public class Vehicle {
    public double calculateValue() {...}
}
public class Car extends Vehicle {
    public double calculateValue() {
        return this.getValue() * 0.8;
}
public class Truck extends Vehicle{
    public double calculateValue() {
        return this.getValue() * 0.9;
}
```
## Liskov Substitution Principle
It tells that the object of the superclass should easily be replaced by object of subclass without breaking the code.

Consider an example of `Rectangle` base class and `Square` derived class.
```java
public class Rectangle {
    private double height;
    private double width;
    public void setHeight(double h) { height = h; }
    public void setWidht(double w) { width = w; }
    ...
}
public class Square extends Rectangle {
    public void setHeight(double h) {
        super.setHeight(h);
        super.setWidth(h);
    }
    public void setWidth(double w) {
        super.setHeight(w);
        super.setWidth(w);
    }
}
```
The above classes do not obey LSP because you cannot replace the `Rectangle` base class with its derived class `Square`. The `Square` class has extra constraints, i.e., the height and width must be the same. Therefore, substituting `Rectangle` with `Square` class may result in unexpected behavior.

## Interface Segregation Principle 
The **Interface Segregation Principle (ISP)** states that clients should not be forced to depend upon interface members they do not use. In other words, do not force any client to implement an interface that is irrelevant to them.

Suppose there’s an interface for vehicle and a `Bike` class:
```java
public interface Vehicle {
    public void drive();
    public void stop();
    public void refuel();
    public void openDoors();
}
public class Bike implements Vehicle {

    // Can be implemented
    public void drive() {...}
    public void stop() {...}
    public void refuel() {...}
    
    // Can not be implemented
    public void openDoors() {...}
}
```
As you can see, it does not make sense for a Bike class to implement the openDoors() method as a bike does not have any doors! To fix this, ISP proposes that the interfaces be broken down into multiple, small cohesive interfaces so that no class is forced to implement any interface, and therefore methods, that it does not need.

## Dependency Inversion Principle 
The **Dependency Inversion Principle (DIP)** states that we should depend on abstractions (interfaces and abstract classes) instead of concrete implementations (classes). The abstractions should not depend on details; instead, the details should depend on abstractions.

Consider the example below. We have a `Car` class that depends on the concrete `Engine` class; therefore, it is not obeying DIP.
```java
public class Car {
    private Engine engine;
    public Car(Engine e) {
        engine = e;
    }
    public void start() {
        engine.start();
    }
}
public class Engine {
   public void start() {...}
}
```
The code will work, for now, but what if we wanted to add another engine type, let’s say a diesel engine? This will require refactoring the `Car` class.
However, we can solve this by introducing a layer of abstraction. Instead of `Car` depending directly on `Engine`, let’s add an interface:
```java
public interface Engine {
    public void start();
}
```
Now we can connect any type of Engine that implements the `Engine` interface to the `Car` class:
```java
public class Car {
    private Engine engine;
    public Car(Engine e) {
        engine = e;
    }
    public void start() {
        engine.start();
    }
}
public class PetrolEngine implements Engine {
   public void start() {...}
}
public class DieselEngine implements Engine {
   public void start() {...}
}
```