SOLID Design Principles

Anjana Dinethra
6 min readFeb 24, 2022

Single Responsibility Principle

what is meant by the single responsibility principle is each and every class should have a single responsibility. setting multiple responsibilities to a single class will throw issues in the code. There can be as many methods as you like in the class if they are related to the other methods in the class.

This Principle is not only applied for classes it can be applied to software components, microservices, APIs, Functions, etc

Let’s find out what are the advantages of using the single responsibility principle,

  • Flexibility
  • Better code quality
  • Maintainability
  • Testability
  • Extensibility

examples:

fig1.0:-Instance where single responsibility principle is not applied

In the above example, methods that are there in the class are not related to each other. HotelReservations class has more than one responsibility. so we can apply the single responsibility principle to the above example. let’s see what it looks like after applying the principle.

fig 1.2
fig 1.3
fig 1.4

Open/Close Principle

Open/Close principle means a module should be open for extension but should be closed for modification. It can be a class, software component, function, etc…

In simple terms, if we want to add new functionality we should not modify the Classes or methods inside them. we can add new functionality by extending it with new classes, methods, etc…

Let’s find out what are advantages we have when following the open/close principle.

  • Reduce the risk when introducing new functionality
  • Existing functionality will not be broken
  • Easier to debug the code
  • Improved performance

examples:

fig 2.0:-Instance where open/close principle is not applied

In the above example, the Open/Close principle is not applied. what we can do to solve it is we can create an interface and we can break the functionality into derived classes then we can implement the interfaces on those derived classes.

fig 2.2:IHotelPaymentService Interface

so that we don’t have to modify the class. when we need to add a new functionality simply we can implement the interface and add the new functionality accordingly.

fig 2.3
fig 2.4

Liskov Substitution Principle

Liskov Substitution principle means child classes must be completely substitutable for their parent classes for example if there is a Parent class called “A”, and “B” is a child class of “A”.we should be able to replace the functionality of class “A” with class “B”.

Let’s find out what are the advantages we can have when we use the Liskov substitution principle.

  • Code Reusability
  • Better inheritance hierarchies
  • Easy maintainability
  • Improved performance

examples:

fig 3.1:MobilePhones Abstract Class
fig 3.2:AndroidPhone Class when principle is not applied

In the above example, we can see that the parent class(MobilePhones) cannot be replaced by the child class(AndroidPhone). It is not following the Liskov substitution principle.

fig 3.3:IOSPhone Class when principle is not applied

As in the previous example, we can see that the parent class(MobilePhones) cannot be replaced by the child class(IosPhone).It also does not follow the Liskov substitution principle as in the earlier case.

Now let’s see what we can do to overcome this issue

we can create separate interfaces or abstract classes but creating interfaces is the most preferred way.

fig 3.4:IMobilePhones Interface
fig 3.5:IAirDropManager Interface
fig 3.6:IDualSimSupportManager Interface

After creating interfaces we can use them in child classes. So that child classes will be able to replace parent classes and the Liskov substitution principle can be maintained.

fig 3.7:AndroidPhone Class after principle is applied
fig 3.8:IOSPhone Class after principle is applied

Interface Segregation Principle

Interface Segregation Principle means that no code should be forced to depend on methods it does not use. we should not force the client to use the methods that they do not want to use.

This principle is very similar to the single responsibility principle. According to the principle, larger interfaces should be broken into smaller interfaces. so that implementation classes can use only the methods that are required.

let us find out the advantages of using the interface segregation principle.

  • Increase Readability
  • Easy maintainability
  • Removes unnecessary code

example:

fig 4.1:AndroidPhone Class when principle is not applied

In the above example we can see that AndroidPhone class is forced to use methods like FaceTime(), AirDrop() even they are not used by AndroidPhone Class.

So we can say that the above example is not following the Interface Segregation Principle

Now let’s see what we do to avoid the above situation,

We can simply create separate interfaces and use them accordingly. So that classes will not be forced to use classes they don’t use and the Interface Segregation Principle can be maintained.

fig 4.2:IDualSimSupportManager Interface
fig 4.3:IMobilePhones Interface
fig 4.4:AndroidPhone Class after principle is applied

Dependency Inversion Principle

Dependency Inversion Principle means High level modules, which provide complex logic, cannot be affected by the changes in low level modules. In simple terms, we should use abstraction and interfaces without using concrete implementations.

Let’s find out what are the advantages we can have when we use the Dependency Inversion Principle.

  • Flexibility
  • Stability
  • Increased Security
  • Code Reusability

examples:

Let’s think of a scenario that we have two classes for payments (MobilePay Class, CreditCard Class) and also lets think we have a service class called PaymentGateway.

fig 5.1:MobilePayments Class
fig 5.2:CreditCard Class

Now as an example lets use MobilePayments Class in our Service Class which is PaymentGateway.

fig 5.3:PaymentGateway Class before applying the principle

In the above example if need to use CreditCardPayments class, we have to change the code all from the beginning which is not the correct way to do it and it can be a very tedious task.

What we can do to solve the above problem is that we can apply dependency inversion principle.

so that payment gateway(high level module) will not be depend on MobilePayments and CreditCardPayment classes(low level modules). For that we can create a common interface for low level modules and use it in the service class(PaymentGateway).

fig 5.4:IPayments Interface
fig 5.5:PaymentGateway Class after applying the principle

--

--