SOLID Principles: A Must-Know for All Developers
Tags: javascript, programming, softwareengineering, oop
Published: 2024-01-29 09:47:50

If you are a software developer wishing to write code without those pesky bugs, let me introduce you to the SOLID principles – your new best friends in the coding world! 

So, if you're dreaming of writing code that's super clear, works like a charm, and makes you feel like a coding superhero, then dive into the SOLID principles. They're here to make your coding journey a whole lot cooler! 🌟

What are the SOLID principles? Let's dive into a universally applicable principle for all developers.

The SOLID principles constitute a set of five design principles for crafting maintainable and scalable software. Introduced by Robert C. Martin, these principles are widely employed in object-oriented programming.

The SOLID acronym encapsulates a set of five guiding principles:

1. S — Single responsibility principle
2. O — Open closed principle
3. L — Liskov substitution principle
4. I — Interface segregation principle
5. D — Dependency Inversion principle

**Single Responsibility Principle (SRP):**
The initial principle asserts that a class, module, or function should have a singular purpose. For example, if a function is utilized to display the model of a car, it shouldn't disclose details about the brand or the company that manufactured the car. Here's a JavaScript example:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lltef4ripbgjtn3m8vnc.png)

this function violets the principle, so instead we must do it like this: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r5668t7bwn1wzgkaodsx.png)

**Open-Closed Principle (OCP):**
OCP dictates that classes, modules, and functions should extend the functionality of a class or function by adding more code without modifying existing code. In simpler terms, software entities should be open for extension but closed for modification. This implies that new functionalities can be added to the system without altering its existing code.
following is an example which violets the principle: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtzyr069t0zyvowzf6u5.png)
In this example, if we want to add a new way of printing details (e.g., HTML format), we would need to modify the Car class and potentially modify existing printing methods. This violates the Open/Closed Principle because the class is not closed for modification.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ok1h04yof134yu36hsg.png)
In this corrected example, we introduced two separate printer classes (ConsolePrinter and HTMLPrinter). Each printer class has its own way of printing details. Now, if we want to add a new way of printing details (e.g., JSON format), we can easily do so by adding a new printer class without modifying the existing code. This adheres to the Open/Closed Principle by being open for extension but closed for modification.

**Liskov Substitution Principle (LSP):**
This principle stipulates that a child class must be capable of replacing the parent class. In other words, objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. The LSP is crucial in object-oriented programming and was introduced by computer scientist Barbara Liskov in 1987.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2vr34gluv469p6cnffht.png)
In this example, the Penguin class inherits from Bird, but it overrides the fly method to throw an error. This violates the Liskov Substitution Principle because objects of the derived class (Penguin) should be substitutable for objects of the base class (Bird) without affecting the correctness of the program.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ayu636c8mvi2vrv866n.png)
In this corrected example, we introduced a new method (swim) to the Penguin class, making it more specialized. The makeBirdFly function checks if the bird is an instance of the base class (Bird) before attempting to make it fly. This adheres to the Liskov Substitution Principle, as now the Penguin class is not violating the expected behavior of the base class.

**Interface Segregation Principle (ISP):**
Similar to the Single Responsibility Principle, ISP asserts that clients should not be compelled to implement interfaces or methods they do not use. Specifically, the ISP suggests breaking down large interfaces into smaller, more specific ones, allowing clients to depend only on the interfaces that are relevant to them. This practice simplifies the codebase, akin to how you organize components in frontend frameworks like React, Svelte, and Vue.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uxxnwy79ekz6a4yvo9bm.png)
In this example, the Programmer class inherits from the Worker class, which includes methods for generic work and taking a break. However, a programmer may not necessarily need the takeBreak method.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75uh7o8ky69od25otug9.png)
In this corrected example, we separated the concerns by introducing the BreakTaker interface. Now, the Programmer class only inherits from what it needs (Worker). If someone needs the break functionality, they can implement the BreakTaker interface separately. This adheres to the Interface Segregation Principle by preventing classes from being forced to implement interfaces or methods they don't use.

**Dependency Inversion Principle (DIP):**
DIP posits that high-level modules should not depend on low-level modules; both should depend on abstractions. In essence, rather than writing code reliant on specific details of how lower-level code works, you should write code dependent on more general abstractions that can be implemented in various ways. Moreover, abstractions should not depend on details; details should depend on abstractions.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0s48demjsw1ss1cd14qn.png)
In this example, the Switch class depends directly on the concrete implementation of LightBulb, violating the Dependency Inversion Principle. The high-level module (Switch) depends on the low-level module (LightBulb).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0izbxa5eu04891v1sso.png)
In the corrected example, we introduced an abstraction (SwitchableDevice) that both LightBulb and Switch depend on. Now, the Switch class depends on the abstraction rather than the concrete implementation, adhering to the Dependency Inversion Principle. This allows for flexibility and easier extension without modifying existing code.

**Conclusion**
In conclusion, the SOLID principles provide a robust foundation for designing maintainable and scalable software systems. These principles - Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion - serve as guiding principles for object-oriented design, encouraging developers to create code that is flexible, extensible, and easy to understand.