# SOLID Design Principle Explained with Java

## Introduction

SOLID design principle is one of the most popular set of design principles in object-oriented software development. It’s a mnemonic acronym for the following five design principles:

• S – Single Responsiblity Principle
• O – Open Closed Principle
• L – Liskov Substitution Principle
• I – Interface Segregation Principle
• D – Dependency Inversion Principle

Now I will explain in details on each of the above design principle with example.

## Single Responsibility Principle

A class should have one and only one reason to change, meaning that a class should do only one job.

It is one of the basic principles most developers apply to build robust and maintainable software.

The most important benefit you get is it makes your software easier to implement and prevents unexpected side-effects of future changes.

Let’s take an example, let’s say you have below shape classes – `Circle` and `Square` and you want to sum all of the calculated area for each shape.

The code for `Circle` class is given below:

``````public public class Circle {
public Circle(double radius) {
}
public double getRadius() {
}
}``````

The code for `Square` class is given below:

``````public class Square {
private double length;
public Square(double length) {
this.length = length;
}
public double getLength() {
return length;
}
}``````

Now you move on by creating the `AreaCalculator` class and then write up your logic to sum up the areas for all provided shapes.

``````public class AreaCalculator {
private Object[] shape;
public AreaCalculator(Object[] shape) {
this.shape = shape;
}
public double calculateTotalArea() {
double sum = 0;
for (int i = 0; i < shape.length; i++) {
if (shape[i] instanceof Circle) {
Circle circle = (Circle) shape[i];
sum += 3.14 * radius * radius;
} else if (shape[i] instanceof Square) {
Square square = (Square) shape[i];
double length = square.getLength();
sum += length * length;
}
}
return sum;
}
public void display() {
System.out.println("Calculated Total Area: " + calculateTotalArea());
}
}``````

To use the `AreaCalculator` class, you simply instantiate the class and pass in an array of shapes, and display the output of the total calculated area.

Now you create the below class `SolidTest` to test the calculated total area for the given shapes.

``````public class SolidTest {
public static void main(String[] args) {
Circle circle = new Circle(5.0d);
Square square = new Square(5);
Object[] arr = new Object[] { circle, square };
AreaCalculator areaCalculator = new AreaCalculator(arr);
areaCalculator.display();
}
}``````

Now look at the class `AreaCalculator`, it does multiple responsibilities – summing up all the areas for shapes as well as displaying total areas.

Now the problem with the `display()` method is that the `AreaCalculator` handles the logic to display the data.

Therefore,

what if the user wanted to display the data as JSON format or something else?

All of that logic would be handled by the `AreaCalculator` class, this is what SRP (Single Responsibility Principle) complains against; the `AreaCalculator` class should only sum the areas for the given shapes, it should not care whether the user wants to display in JSON or in HTML format.

Therefore there is a reason to break down the class `AreaCalculator` and it should do only one job – calculating areas.

To fix this you can create an `AreaCalculatorOutputter` class and use this to handle whatever logic you need to handle how the sum areas of all given shapes are displayed.

``````public class AreaCalculatorOutputter {
private double totalArea;
public AreaCalculatorOutputter(double totalArea) {
this.totalArea = totalArea;
}
public String json() {
return totalArea + " in json format";
}
public String html() {
return totalArea + " in html format";
}
public String xml() {
return totalArea + " in xml format";
}
}``````

Remove the `display()` method from `AreaCalculator` class. The updated `AreaCalculator` class is shown below:

``````public class AreaCalculator {
private Object[] shape;
public AreaCalculator(Object[] shape) {
this.shape = shape;
}
public double calculateTotalArea() {
double sum = 0;
for (int i = 0; i < shape.length; i++) {
if (shape[i] instanceof Circle) {
Circle circle = (Circle) shape[i];
sum += 3.14 * radius * radius;
} else if (shape[i] instanceof Square) {
Square square = (Square) shape[i];
double length = square.getLength();
sum += length * length;
}
}
return sum;
}
}``````

Now updated `SolidTest` class looks like below:

``````public class SolidTest {
public static void main(String[] args) {
Circle circle = new Circle(5.0d);
Square square = new Square(5);
Object[] arr = new Object[] { circle, square };
AreaCalculator areaCalculator = new AreaCalculator(arr);
double totalArea = areaCalculator.calculateTotalArea();
AreaCalculatorOutputter areaCalculatorOutputter = new AreaCalculatorOutputter(totalArea);
System.out.println(areaCalculatorOutputter.json());
System.out.println(areaCalculatorOutputter.html());
System.out.println(areaCalculatorOutputter.xml());
}
}``````

Now, whatever logic you need to display the data to the user is now handled by the `AreaCalculatorOutputter` class.

With this solution, you have some classes but each class with a single responsibility so you get a low coupling and a high cohesion.

## Open Closed Principle

The Open Closed Principle states that:

Software entities, classes, methods etc. should be open for extension but closed for modification.

To put this more concretely, you should write a class that does what it needs to flawlessly and not assuming that people should come in and change its source code later. Its source code is closed for modification, but modification to the entities behavior can be achieved by extending its features, for instance, by inheriting from it and overriding or extending certain behaviors.

In the previous section’s example, let’s take a look at the `AreaCalculator` class, especially it’s `calculateTotalArea()` method.

If you wanted the `calculateTotalArea()` method to be able to sum areas of more shapes, you would have to add more if/else blocks.

As new types of shapes come to calculate area, the `AreaCalculator` class will be more confusing and fragile to changes.

Therefore the `AreaCalculator` class is not closed for modification and that goes against the Open-closed principle.

A way you can make the shape as abstract class or interface and remove the logic to calculate the area of each shape out of the `calculateTotalArea()` method and attach it to the shape’s class.

You can create below `Shape` interface or if you want you may also create abstract class:

``````public interface Shape {
double area();
}``````

Now updated `Circle` class is given below:

``````public class Circle implements Shape {
public Circle(double radius) {
}
@Override
public double area() {
}
}``````

Similarly, updated `Square` class is given below:

``````public class Square implements Shape {
private double length;
public Square(double length) {
this.length = length;
}
@Override
public double area() {
return length * length;
}
}``````

Now look at the below `AreaCalculator` class:

``````public class AreaCalculator {
private Shape[] shapes;
public AreaCalculator(Shape[] shapes) {
this.shapes = shapes;
}
public double calculateTotalArea() {
double sum = 0;
for (int i = 0; i < shapes.length; i++) {
sum += shapes[i].area();
}
return sum;
}
}``````

Now the `AreaCalculator` class remains intact when we add a new shape type. The existing code is not modified.

So if you want to add more types of shapes you just have to create a class for that shape.

Now calculate the total area of all shapes:

``````public class SolidTest {
public static void main(String[] args) {
Circle circle = new Circle(5.0d);
Square square = new Square(5);
Shape[] arr = new Shape[] { circle, square };
AreaCalculator areaCalculator = new AreaCalculator(arr);
double totalArea = areaCalculator.calculateTotalArea();
AreaCalculatorOutputter areaCalculatorOutputter = new AreaCalculatorOutputter(totalArea);
System.out.println(areaCalculatorOutputter.json());
System.out.println(areaCalculatorOutputter.html());
System.out.println(areaCalculatorOutputter.xml());
}
}``````

Now when you run the above main class, you will see below output:

``````103.5 in json format
103.5 in html format
103.5 in xml format``````

## Liskov Substitution Principle

Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

All this stating is that every subclass/derived class should be substitutable for their base/parent class.

It ensures that derived classes do not affect the behavior of the classes they are inheriting from. Or put another way, that any derived class can take the place of its parent class.

Let’s consider the below example.

You have a below `Person` class:

``````public class Person {
void walkInsideRoom() {
System.out.println("Walk inside the room");
}
void walkOutsideRoom() {
System.out.println("Walk outside the room");
}
}``````

Now, a prisoner is obviously a person. So logically, a sub-class can be created:

``````public class Prisoner extends Person {
void walkOutsideRoom() {
throw new RuntimeException("Cannot walk outside the room");
}
}``````

Also obviously, this leads to trouble, since a prisoner is not free to move an arbitrary distance in any direction, yet the contract of the `Person` class states that a Person can.

Thus, the class `Person` could better be named `FreePerson`. If that were the case, then the idea that class `Prisoner` extends `FreePerson` is clearly wrong.

As per the inheritance hierarchy the `Person` object can point to any one of its child objects and you do not expect any unusual behavior.

But when `walkOutsideRoom()` method of the `Prisoner` object is invoked it leads to below error because our `Prisoner` object does `walkOutsideRoom()` the room but they are not allowed to.

So Liskov principle says the parent should easily replace the child object.

Therefore to implement Liskov you need to create two interfaces – one is for walk inside and other for walk outside as shown below.

The below interface allows walking inside

``````public interface InsideRoom {
void walk();
}``````

The below interface allows walking outside:

``````public interface OutsideRoom {
void walk();
}``````

Now the `Prisoner` class will only implement `InsideRoom` interface as he/she is not allowed to go outside the room.

``````public class Prisoner implements InsideRoom {
@Override
public void walkInside() {
System.out.println("Walk inside the room");
}
}``````

While the Person class will implement both `InsideRoom` interface as well as `OutsideRoom` as he/she is free to walk anywhere.

``````public class Person implements InsideRoom, OutsideRoom {
@Override
public void walkInside() {
System.out.println("Walk inside the room");
}
@Override
public void walkOutside() {
System.out.println("Walk outside the room");
}
}``````

Therefore from the above situation you got to know that this strongly suggests that inheritance should never be used when the sub-class restricts the freedom implicit in the base class, but should only be used when the sub-class adds extra detail to the concept represented by the base class.

## Interface Segregation Principle

Clients should never be forced to implement interfaces that they don’t use.

In other words, make fine grained interfaces that are client specific.

In our previous example while I was discussing about SRP and OCP, then I used `Shape` interface to give the solution.

Now let’s say new clients come up with a demand that the client wants to add new method that will calculate volume for shapes in addition to area calculation.

So you enthusiastically update the Shape by adding a new method called volume.

``````public interface Shape {
double area();
double volume();
}``````

Any shape you create must implement the volume method, but you know that squares or circles are flat shapes and they do not have volumes, so this interface would force the `Square` or `Circle` class to implement a method that has no use.

It goes against ISP (Interface Segregation Principle) because you are forcing them to use the volume method, instead you could create another interface called `SolidShape` that has the volume contract and solid shapes like cubes, spheres etc. can implement this interface:

``````public interface SolidShape {
double volume();
}``````

Now let’s say a solid shape `Cude` implements `SolidShape` interface:

``````public class Cube implements SolidShape {
private double side;
public Cube(double side) {
this.side = side;
}
@Override
public double volume() {
return side * side * side;
}
}``````

Another solid shape `Sphere` implements `SolidShape` interface:

``````public class Sphere implements SolidShape {
public Sphere(double radius) {
}
@Override
public double volume() {
return (4 / 3) * 3.14 * radius * radius * radius;
}
}``````

Now if you want to calculate area as well volume of a specific shape, such as, `Cuboid` then you can create class that implements both `Shape` and `SolidShape` interfaces:

``````public class Cuboid implements Shape, SolidShape {
private double height;
private double width;
private double length;
public Cuboid(double height, double width, double length) {
this.height = height;
this.width = width;
this.length = length;
}
@Override
public double volume() {
return height * width * length;
}
@Override
public double area() {
return 2 * (width * length + width * height + length * height);
}
}``````

Therefore, by breaking down interfaces, you favor Composition instead of Inheritance, and Decoupling over Coupling.

You favor composition by separating by roles(responsibilities) and decoupling by not coupling derivative classes with unneeded responsibilities inside a monolith.

### Dependency Inversion Principle

Entities must depend on abstractions not on concretions.

It means that the high level module must not depend on the low level module, but they should depend on abstractions.

Let’s say you have a system that handles authentication through external services such as Google, Facebook, Twitter etc. You would have a class for each service: GoogleAuthenticationService, GitHubAuthenticationService, etc.

To be able to make use of all the services, you have two possibilities: either write a piece of code that adapts each service to the authentication process, or define an abstraction of the authentication services.

The first possibility is a dirty solution that will potentially introduce technical debt in the future as shown in the below example; in case a new authentication service is to be integrated to the system, you will need to change the code, which as a result violates the OCP.

``````public class GoogleAuthenticationService {
}``````
``````public class Authenticator {
public Authenticator(GoogleAuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
}``````

First the `GoogleAuthenticationService` is the low level module while the `Authenticator` is high level, but according to the definition of D in S.O.L.I.D. which states that “depend on Abstraction but not on Concretions”, this snippet above violates this principle as the `Authenticator` class is being forced to depend on the `GoogleAuthenticationService` class.

Later if you were to change the authentication service, you would also have to edit the `Authenticator` class and thus violates Open Close Principle.

The `Authenticator` class should not care what authentication service your application uses, to fix this again you need to “code to an interface”, since high level and low level modules should depend on abstraction.

This second possibility is much cleaner, it allows for future addition of services, and changes can be done to each service without changing the integration logic.

By defining a `AuthenticationService` interface and implementing it in each service, you would then be able to use Dependency Injection in our authentication logic.

``````public interface AuthenticationService {
boolean isAuthenticated(Object user);
}``````

The interface has a `isAuthenticated()` method and the `GoogleAuthenticationService` class implements this interface, also instead of directly type-hinting `GoogleAuthenticationService` class in the constructor of the `Authenticator`, you instead type-hint the interface and no matter the type of authentication service your application uses, the `Authenticator` class can easily authenticate the user without any problems and OCP is not violated.

``````public class GoogleAuthenticationService implements AuthenticationService {
@Override
public boolean isAuthenticated(Object user) {
// return true if user is authenticated
return false;
}
}``````

Now the updated `Authenticator` class should look like this:

``````public class Authenticator {
private AuthenticationService authenticationService;
public Authenticator(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
}``````

According to the little snippet above, you can now see that both the high level and low level modules depend on abstraction.

By depending on higher-level abstractions, you can easily change one instance with another instance in order to change the behavior.

Dependency Inversion increases the reusability and flexibility of our code.

That’s all about SOLID design principle.