SOLID Principles in Java with Examples

In this article, we will discuss what are SOLID principles in Java with examples.

The SOLID principles were first conceptualized by Robert C. Martin in his 2000 paper, Design Principles, and Design Patterns. These concepts were later built upon by Michael Feathers, who introduced us to the SOLID acronym.
So, what is SOLID and how does it help us write better code? Simply put, Martin's and Feathers' design principles encourage us to create more maintainable, understandable, and flexible software. Consequently, as our applications grow in size, we can reduce their complexity and save ourselves a lot of headaches further down the road!

The following 5 concepts make up our SOLID principles:
  1. Single Responsibility
  2. Open/Closed
  3. Liskov Substitution
  4. Interface Segregation
  5. Dependency Inversion
Let's understand each of these SOILD principles with an example. I have written a separate article for each SOLID principle to explain it with bad and good design examples.

These 5 SOLID principles are the most recommended design principles and we should keep in mind while writing our classes. They also form the best practices to be followed for designing our application classes.

1. Single Responsibility Principle

“One class should have one and only one responsibility”
Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. There should never be more than one reason for a class to change.
The Single Responsibility Principle represents the “S” of the five SOLID Principles of object-oriented programming to writing well-designed code that is more readable, maintainable, and easier to upgrade and modify.

Rules of Thumb?

  • If you cannot come up with a meaningful name for your class focused on single responsibility, then it's probably doing too much.
  • Every object in our web application should have a single responsibility, and all object's services should be focused on carrying that single responsibility(SRP).
  • If you put more than one functionality in one Class in Java it introduces coupling between two functionality and even if you change one functionality there is a chance you broke coupled functionality, which requires another round of testing to avoid any surprise on the production environment.
Read more about the Single Responsibility Principle with an example at Single Responsibility Principle in Java with Example.

2. Open Closed Principle

This is the second important rule which we should keep in mind while designing our application. Open closed principle states:

“Software components should be open for extension, but closed for modification”
Simply put, Software entities like classes, modules, and functions should be open for extension but closed for modifications. 

The Open-Closed Principle represents the “O” of the five SOLID Principles of object-oriented programming to writing a well-designed code that is more readable, maintainable, and easier to upgrade and modify.

Rules of Thumb?

  • Open to an extension - you should design your classes so that new functionality can be added as new requirements are generated.
  • Closed for modification - Once you have developed a class you should never modify it, except to correct bugs.
  • Design and code should be done in a way that new functionality should be added with minimum or no changes in the existing code
  • When needs to extend functionality - avoid tight coupling, don't use if-else/switch-case logic, do code refactoring as required.
  • Techniques to achieve - Inheritance, Polymorphism, Generics
  • Pattern to apply – Strategy Pattern, Template Method
Let's learn this design principle with class diagrams, hands-on source code, thumb rules, benefits at Open Closed Principle in Java with Example

3. Liskov's Substitution Principle

This principle says:
"Derived types must be completely substitutable for their base types."
The Liskov Substitution Principle represents the “L” of the five SOLID Principles of object-oriented programming to writing well-designed code that is more readable, maintainable, and easier to upgrade and modify.

Rules of Thumb?

  • This principle applies to inheritance hierarchies and is just an extension of the Open-Closed Principle.
  • It means that we must make sure that new derived classes are extending the base classes without changing their original behavior. Basically, derived classes should never do less than their base class.
  • If a subtype of the supertype does something that the client of the supertype does not expect, then this is in violation of LSP. Imagine a derived class throwing an exception that the superclass does not throw, or if a derived class has some unexpected side effects. One has to consider how the client programs are using the class hierarchy. Sometimes code refactoring is required to fix identified LSP violations.
Read more about the Liskov Substitution Principle with an example at the Liskov Substitution Principle in Java with Example.

4. Interface Segregation Principle

This principle is applicable to interfaces as a single responsibility principle holds to classes. ISP says:
“Clients should not be forced to implement unnecessary methods which they will not use”
The ‘I ‘ in SOLID stands for interface segregation, and it simply means that larger interfaces should be split into smaller ones. By doing so, we can ensure that implementing classes only need to be concerned about the methods that are of interest to them.
Read more about interface segregation principle with an example at the Interface Segregation Principle in Java with Example.

5. Dependency Inversion Principle

The principle states:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.

The Dependency Inversion Principle represents the “D” of the five SOLID Principles of object-oriented programming to writing well-designed code that is more readable, maintainable, and easier to upgrade and modify.

Rules of Thumb?

  • Design by contract.
  • Every dependency in the design should target an interface or an abstract class. No dependency should target a concrete class.
  • Factories and Abstract Factories can be used as dependency frameworks, but there are specialized frameworks for that such as Spring IOC (Inversion of Control Container).
Read more about the Dependency Inversion Principle at the Dependency Inversion Principle in Java with Example

Conclusion

In this article, we've taken a deep dive into the SOLID principles of object-oriented design.

The source code examples of each SOLID principles available over on GitHub.

Comments