Skip to main content

From Spaghetti to SOLID: Crafting Maintainable Code

The SOLID principles are five key design principles in object-oriented programming aimed at making software designs more understandable, flexible, and maintainable. They were popularized by Robert C. Martin (Uncle Bob).

Here's what each letter stands for:

S - Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change. In simpler terms, a class should focus on a single job or functionality.

Why it's important: It makes code easier to understand and test. Helps in separating concerns clearly. Enhances reusability and maintainability.

Violating SRP:

This class has three responsibilities: Business logic, Presentation and Persistence.



Applying SRP:

Each class has a single reason to change. You can test, maintain, and extend each one independently.



O - Open/Closed Principle (OCP)

Definition: Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. You should be able to add new behavior without changing existing code. Encourages designing code that is extensible but stable.

Why it's important: Encourages the use of polymorphism and inheritance or composition. New behavior can be added with minimal risk of breaking existing code.

Violating OCP:

Every new type requires modifying this method.



Applying OCP:

Want to add WhatsApp? Just create a WhatsAppSender class. No need to change NotificationService.



L - Liskov Substitution Principle (LSP)

Definition: Objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program. A subclass should behave like its parent class.

Why it's important: Ensures polymorphism works as intended. Violations often result in bugs or broken contracts in inheritance hierarchies.

Violating LSP:

Ostrich breaks the expectation that all Birds can fly. It violates LSP because client code using Bird has to do instanceof or catch exceptions.



Applying LSP:

Now, Ostrich implements Bird, but not FlyingBird, keeping the substitution safe.



I - Interface Segregation Principle (ISP)

Definition: Clients should not be forced to depend on interfaces they do not use. ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them.

Why it's important: Encourages the creation of fine-grained interfaces. Avoids "fat interfaces" that force classes to implement irrelevant methods.

Violating ISP:

BasicPrinter is forced to implement irrelevant methods.



Applying ISP:

OldPrinter only implements Printer. MultiFunctionPrinter implements all relevant interfaces.



D - Dependency Inversion Principle (DIP)

Definition: 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.

Why it's important: Reduces tight coupling between modules. Promotes use of interfaces or abstract classes. Makes unit testing easier via mocking.

Violating DIP:

ReportService depends on a concrete class. We cannot easily swap the database. Cannot mock or stub for testing without rewriting code.



Applying DIP:

Now DataService can use any implementation of Database (e.g., PostgreSQL, MongoDB), making it more flexible and testable.



Summary

SOLID is a set of five design principles — SRP, OCP, LSP, ISP, DIP — that promote clean, maintainable, and scalable object-oriented software, enabling better modularity, testability, and flexibility.

Principle Focus Benefit
SRP One responsibility per class Easy to understand and change
OCP Extend without modifying Safer, flexible code evolution
LSP Subtypes replace base types Correct use of polymorphism
ISP Avoid fat interfaces Clearer, minimal contracts
DIP Depend on abstractions Loose coupling, easier testing

Comments

Popular posts from this blog

Deploying Spring Boot microservices on Kubernetes Cluster

This article guides you through the deployment of two Spring Boot microservices, namely "order-service" and "inventory-service," on Kubernetes using "MiniKube" . We will establish communication between them, with "order-service" making calls to an endpoint in "inventory-service." Additionally, we will configure "order-service" to be accessible from the local machine's browser . 1) Create Spring Boot microservices The Spring Boot microservices, "order-service" and "inventory-service," have been developed and can be found in this GitHub repository. If you are interested in learning more about creating Spring Boot REST microservices, please refer to this or this (Reactive) link. 2) Build Docker Images The Docker images for both "order-service" and "inventory-service" have already been generated and deployed on DockerHub, as shown below. codeburps/order-service cod...

Circuit Breaker Pattern with Resilience4J in a Spring Boot Application

Read Also: Spring Cloud Circuit Breaker + Resilience4j Resilience4j is a lightweight fault tolerance library that draws inspiration from Netflix Hystrix but is specifically crafted for functional programming. The library offers higher-order functions, known as decorators , designed to augment any functional interface, lambda expression, or method reference with features such as Circuit Breaker, Rate Limiter, Retry, or Bulkhead . These functionalities can be seamlessly integrated within a project, class, or even applied to a single method. It's possible to layer multiple decorators on any functional interface, lambda expression, or method reference, allowing for versatile and customizable fault tolerance. While numerous annotation-based implementations exist online, this article focuses solely on the reactive approach using router predicates and router functions . How Circuit Breaker Pattern works? In general, a circuit breaker functions as an automatic electrical s...

How to create a basic Spring 6 project using Maven

Below is a step-by-step guide to creating a basic Spring project using Maven. 1) Create a Maven Project Use the following Maven command to create a new Maven project. mvn archetype:generate -DgroupId=com.tb -DartifactId=spring-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 2) Import in IntelliJ IDEA If you haven't already, open IntelliJ IDEA on your system. Go to "File" > "New" > "Project from Existing Sources..." . In the file dialog, navigate to the directory where your Maven project is located. Select the pom.xml file within the project directory and click "Open." 3) Update pom.xml In total, the application requires the below-mentioned dependencies: 4) Create Spring Configuration Create a Java configuration class that uses annotations to define your Spring beans and their dependencies. This class should be annotated with @Configuration . 5) Create the Main Application C...