SOLID Principles in Java
Tags: java, beginners, computerscience, codenewbie
Published: 2022-01-26 06:14:11

In software engineering, **SOLID** is an acronym for 5 [design principles](https://principles.design/) intended to make software designs more understandable, flexible, robust, and maintainable. Adopting these practices can contribute to avoiding code smells too.

The 5 SOLID principles are:

*   **S** - The single-responsibility principle
*   **O** - The open-closed principle
*   **L** - The Liskov substitution principle
*   **I** - The interface segregation principle
*   **D** - The dependency inversion principle

Although the SOLID principles apply to any programming language, in further section I will be explaining each of them with examples written specifically in JAVA.

## Single Responsibility Principle

This principle states that “**a class should have only one reason to change**” which means every class should have a single responsibility.

```java
public class Vehicle {
    public void details() {}
    public double price() {}
    public void addNewVehicle() {}
}
```

Here the class has multiple reasons to change because the `Vehicle` class has three separate responsibilities: printing details, printing price, and adding a new vehicle to Database.

To achieve the goal of the single responsibility principle, we should implement a separate class that performs a single functionality only.

```java
public class VehicleDetails {
    public void details() {}
}

public class CalculateVehiclePrice {
    public double price() {}
}

public class AddVehicle {
    public void addNewVehicle() {}
}
```

## Open-Closed Principle

This principle states that “**software entities (classes etc.) should be open for extension, but closed for modification**”. This means without modifying anything in a class, it should be extendable.

Let's understand this principle with an example of a notification service

```java
public class NotificationService{
    public void sendNotification(String medium) {
         if (medium.equals("email")) {}
    }
}
```

Here, if you want to introduce a new medium other than email, let's say send a notification to a mobile number then you need to modify the source code in NotificationService class.

So to overcome this you need to design your code in such a way that everyone can reuse your feature by extending it and if they need any customization they can extend the class and add their feature on top of it.

You can create a new interface like:

```java
public interface NotificationService {
    public void sendNotification(String medium);
}
```

Email Notification:

```java
public class EmailNotification implements NotificationService {
    public void sendNotification(String medium){
        // write Logic using for sending email
    }
}
```

Mobile Notification:

```java
public class MobileNotification implements NotificationService {
    public void sendNotification(String medium){
        // write Logic using for sending notification via mobile
    }
}
```

## Liskov Substitution Principle

This principle states that “**derived classes must be able to substitute for their base classes**”. In other words, if class A is a child of class B, then we should be able to replace B with A without interrupting the current behavior of the program.

Consider an example of a square class derived from Rectangle base 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);
    }
}
```

In the `Rectangle` class, setting width and height seems perfectly logical. However, in the square class, the SetWidth() and SetHeight() don't make sense because setting one would change the other to match it.

In this case, Square fails the Liskov substitution test 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

This principle applies to Interfaces and it is similar to the single responsibility principle. It states that “\*\* a client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use.\*\*“.

Let's understand this by the example of a vehicle interface:

```java
public interface Vehicle {
    public void drive();
    public void stop();
    public void refuel();
    public void openDoors();
}
```

Let's say we now create a `Bike` class using this Vehicle interface

```java
public class Bike implements Vehicle {
    public void drive() {}
    public void stop() {}
    public void refuel() {}
    
    // Can not be implemented
    public void openDoors() {}
}
```

Since Bike doesn't have doors we can't implement the last function.

To fix this, it is recommended to break down the interfaces into small multiple, interfaces so that no class is forced to implement any interface or methods, that it does not need.

```java
public interface Vehicle {
    public void drive();
    public void stop();
    public void refuel();
}
```

```java
public interface Doors{
    public void openDoors();
}
```

Creating two classes - `Car` and `Bike`

```java
public class Bike implements Vehicle {
    public void drive() {}
    public void stop() {}
    public void refuel() {}
}
```

```java
public class Car implements Vehicle, Door {
    public void drive() {}
    public void stop() {}
    public void refuel() {}
    public void openDoors() {}
}
```

## Dependency Inversion Principle

The Dependency Inversion Principle (DIP) states that "**entities must depend on abstractions (abstract classes and interfaces), and not on concrete implementations (classes). Also, the high-level module must not depend on the low-level module, but both should depend on abstractions**".

Suppose there is a  book store that enables customers to put their favorite books on a particular shelf.

In order to implement this functionality, we create a `Book` class and a `Shelf` class. The Book class will allow users to see reviews of each book they store on the shelves. The Shelf class will let them add a book to their shelf. For example,

```java
public class Book {
    void seeReviews() {}
}


public class Shelf {
     Book book;
     void addBook(Book book) {}
}
```

Everything looks fine, but as the high-level `Shelf` class depends on the low-level `Book`, the above code violates the Dependency Inversion Principle. This becomes clear when the store asks us to enable customers to add their own reviews to the shelves, too. In order to fulfill the demand, we create a new `UserReview` class:

```java
public class UserReview{
     void seeReviews() {}
}
```

Now, we should modify the Shelf class so that it can accept User Reviews, too. However, this would clearly break the Open/Closed Principle too.

The solution is to create an abstraction layer for the lower-level classes (Book and UserReview). We’ll do so by introducing the Product interface, both classes will implement it. For example, the below code demonstrates the concept.

```java
public interface Product {
    void seeReviews();
}

public class Book implements Product {
    public void seeReviews() {}
}

public class UserReview implements Product {
    public void seeReviews() {}
}
```

Now, the Shelf can reference the Product interface instead of its implementations (Book and UserReview). The refactored code also allows us to later introduce new product types (for instance, Magazine) that customers can put on their shelves, too.

```java
public class Shelf {
    Product product;
    void addProduct(Product product) {}
    void customizeShelf() {}
}
```

## Conclusion

In this article, you were presented with the five principles of the SOLID Code. Adhering to SOLID principles can make your project, extendable, easily modifiable, well tested, with fewer complications.

Happy coding! 💻

(If you find any doubts, updates, or corrections to improve this article, Feel free to share them in the comments) 😊

---

### Starting out in web development?? 💻

Checkout ▶ [HTML To React: The Ultimate Guide](https://gumroad.com/a/316675187)

This ebook is a comprehensive guide that teaches you **everything you need to know to be a web developer through a ton of easy-to-understand examples and proven roadmaps**

It contains 👇

✅  Straight to the point explanations

✅  Simple code examples

✅  50+ Interesting project ideas

✅  3 Checklists of secret resources

✅  A Bonus Interview prep

You can even check out [a free sample from this book](https://drive.google.com/drive/u/0/folders/1GJECqmBUbOwgg5eQvGlMwHcDShqxKISJ)

and here's the [link](https://gumroad.com/a/316675187) with 60% off on the original price on the complete book set ⬇

[![eBook.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618289289910/D--w2qaO3h.png)](https://gumroad.com/a/316675187)