SOLID Design Principles

Solid Principles

S.O.L.I.D. Stands for

  • S — Single responsibility principle
  • O — Open closed principle
  • L — Liskov substitution principle
  • I — Interface segregation principle
  • D — Dependency Inversion principle

S — Single Responsibility Principle

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

Let’s assume we have Rectangle Class. So that it has method and properties. When look at below class, It has a Single responsibility. But if we add Print function on it , then we violate single responsibility. Because printing value is not part of Rectangle class responsibility.

class Rectangle {
 width = 10;
 height = 10;

 CalculateArea = () => {
  return width * height;
 }
}

let rectangleObj = new Rectangle();

In order to Print the calculated area, we should have another class.

class Print{
 write = (text) => {
   console.log(text);
 }
}

let area = rectangleObj. CalculateArea();
let printObject = new Print();
printObject.write(area);

O — Open/Closed Principle

A classes should be open for extension, but closed for modification

Let’s assume we have Rectangle class and that is deployed to production and end-user satisfy with the functionality. After few month late, if end-user would have asked for adding color property, we should not modify existing class to do that. Instead of we should have to extend the class.

class Rectangle {
 width = 10;
 height = 10;

 CalculateArea = () => {
  return width * height;
 }
}

class RectangleWithColor extends Rectangle {
 color = 'red';
}

L — Liskov substitution principle

If class A is a subtype of class B, then we should be able to replace with without disrupting the behavior of our program.

In other words, as simple as that, a subclass should override the parent class methods in a way that does not break functionality from a client’s point of view.

I — Interface segregation principle

Larger interfaces should be split into smaller ones. By doing so, we should never force end-users to implement an interface that does not use or methods they don’t use.

Let’s assume we have Shape interface. We know that if it’s a solid shape we have to calculate volume instead of area. So in below interface, user has to implement both method in their concrete class even they don’t need.

const shapeInterface = (state) => ({
  area: () => state.area(state),
  volume: () => state.volume(state)
})

In this theory, we should have two interfaces in order to avoid forcing end-user to implement methods which are not used.

const shapeInterface = (state) => ({
  area: () => state.area(state)
})

const solidShapeInterface = (state) => ({
  volume: () => state.volume(state)
})

D – Dependency inversion principle

Entities must depend on abstractions not on concretions. So that high level module must not depend on the low level module, but they should depend on abstractions.

In this theory, we should avoid initializing object within the class. All the required object pass though the class constructor. By doing so, the class will be loosely couple and easily unite testable.

Leave a Reply