SOLID Principles

Refresher with sample code examples.

Vithulan MV
Javarevisited
5 min readApr 21, 2022

--

Introduction

SOLID is a software development principle that is guidelines to follow when building software to make it easier to scale and maintain. They were made famous by Robert C. Martin (known as Uncle Bob).

These principles outline best practices for designing software while considering the project’s long-term maintenance and expansion. Adopting these techniques can also help one avoid code smells, restructure one's code, and design Agile or adaptive software.

SOLID stands for:

  • S — Single Responsibility Principle (SRP)
  • O — Open-Closed Principle (OCP)
  • L — Liskov’s Substitution Principle (LSP)
  • I — Interface Segregation Principle (ISP)
  • D — Dependency Inversion Principle (DIP)

SOLID principles complement each other and work together to achieve the common purpose of well-designed software.

Single Responsibility Principle (SRP)

Every software component should have one and only one responsibility.

Here component can be either a Java class, a method, or a module. If we consider a Swiss knife and a regular knife, a traditional knife adheres to the SRP because it has only one responsibility.

Cohesion

Cohesion is how various parts of a software component are related. Higher cohesion helps to attain better adherence to the Single Responsibility Principle

Below Circle class’s calculateArea() method and calculatePerimeter() method are highly cohesive, whereas it has low cohesion with drawCircle() and fillCircle() methods.

Therefore, the methods need to be put in different classes as below.

Coupling

Coupling is defined as the level of interdependency between various software components. Loose coupling helps attain better adherence to the single responsibility principle.

Uncle Bob’s latest definition of SRP

Every software component should have one and only one reason to change.

More reasons to change → More changes in the future → More number of bugs → More money spent.

Open-Closed Principle (OCP)

Software components should be closed for modification but open for extension.

Closed for modification

New features are getting added to the software component; you should NOT have to modify the existing code.

Open for extension

A software component should be extendable to add a new feature or to add new behavior to it.

If we do not follow the open-closed principle, one needs to modify the existing code, and then the QE team needs to test the new and old features with complete regression. It could introduce bugs into the current feature as well.

Liskov’s Substitution Principle (LSP)

Objects should be replaceable with their subtypes without affecting the correctness of the program.

Unimplemented methods are almost always indicated as a design flaw. There are two ways to solve this problem:

  1. Breaking the hierarchy
  2. Tell, don’t ask.

Breaking the hierarchy

Traditionally inheritance is approached using the “Is-A” way of thinking. Liskov wants to move away from the “Is-A” method of creating interfaces.

Formula 1 racing car is a type of car. So, one should be able to extend car class when implementing Formula 1 car.

The above code snippet will not work because F1Car doesn’t have the getCabinWidth( ) method implemented. It only has getCockpitWidth(). This fails Liskov’s test. This problem can be solved by breaking the hierarchy by introducing a Vehicle interface with the more abstract getInteriorWidth() method.

Tell, don’t ask

Below code snippet asks for the object type. Instead it should tell the method to get the interiorWidth(). If one can refactor as the code above, he/she can adhere the Liskov’s substitution principle.

Interface Segregation Principle (ISP)

No client should be forced to depend on methods it does not use

The below code snippet has a lot of unimplemented methods.

The solution to this problem is to segregate the interfaces as below.

Techniques to identify violations of ISP

  1. Fat interfaces
  2. Interfaces with low cohesion
  3. Empty method implementations

Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level methods. Both should depend on abstractions.

Abstractions should not depend on details. Details should depend on abstractions.

The above picture shows that both high-level method ProductCatalog and low-level method SQLProductRepository are dependent on abstraction — ProductRepository. Also, it shows that details depend on the abstraction.

Dependency Injection

Ideally, productCatalog above doesn’t need to worry about when and where to instantiate the object. Therefore, the main program will inject the dependency into productCatalog instead of productCatalog instantiating the dependency. The injection can be done using the constructors.

Inversion of Control (IOC)

This is not a part of the dependency inversion principle. However, it is closely related to it. Usually, the dependency injection is done through the main control thread. We need to take the dependency injection to a separate thread from the main control thread. This can be achieved through frameworks. Spring framework does it with Spring IoC Container.

The Spring IoC container

This covers the concept and rationale behind the SOLID principles.

Thanks for reading.
Happy Coding!

--

--