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

Declarative REST Client in Spring Boot (Spring 6 HTTP Interface)

Feign , an early declarative REST client, was initially part of the Spring Cloud Netflix stack and later rebranded as Spring Cloud OpenFeign . Before its introduction, crafting HTTP calls using RestTemplate involved repetitive code for each service interaction. With Feign, developers could simply define an interface with method contracts mirroring the service's endpoints. Behind the scenes, proxy magic generated a fully functional HTTP client, eliminating the need for boilerplate code . HTTP Interface (Spring Framework 6) The recent release of Spring Framework 6 integrated this declarative REST client as a native part of the core web framework in the form of the HTTP Interface . All the necessary components reside in the spring-web module, which is a transitive dependency for either the spring-boot-starter-web or spring-boot-starter-webflux modules. Currently, the WebFlux dependency is essential due to the HttpServiceProxyFactory , responsible for client gener...