Solid
Last updated
Last updated
💡 SOLID is an acronym introduced by in the early 2000s, representing the five principles you should consider in object-oriented programming. These principles are merely guidelines you can choose to apply in software development, but they allow you to create extensible, flexible, readable systems with clean code (spoiler: we will talk about clean code in future entries). We can conclude that the SOLID principles allow us a high degree of cohesion and low coupling.
Cohesion in terms of computing refers to the degree to which different elements of the same system remain united, generating a larger element. We could see it as a class that integrates several methods, each of which is related to each other, having a common “theme.”
Coupling is the degree to which all these elements are related to each other. The greater the relationships or dependencies, the higher the degree of coupling.
We have seen a bit of theory, and now we will focus on practice. In this part of the article, we will look at how to apply each of the principles in this wonderful language.
By the way, if you are looking to become a better software developer, check out .
S – Single Responsibility Principle
O – Open/Closed Principle
L – Liskov Substitution Principle
I – Interface Segregation Principle
D - Dependency Inversion Principle
It tells us that a class or function should focus on a single responsibility, that there should be a single reason to change; in summary, we can say that this principle asks us that all methods or sub-functions have high cohesion.
In this example, we can see how the Car class has specific methods for reading and writing information, but it does not do anything additional like saving to a database or calling other unrelated functions.
It tells us that we should be able to extend the behavior of a class/function without modifying it.
If we wanted to add the ability to add more products to the PantryProducts
class, we would do the following:
As you can see, we have made modifications to the class without altering the previous functionality, thus complying with the principle.
The principle indicates that if you are using a Rectangle
class and then create another class called Square
that extends from Rectangle
, then any object created from the Rectangle
class can be replaced by Square
, forcing us to ensure that any child class does not alter the behavior of the parent class.
So we would have a rectangle:
And we have a test written in mocha to check the area:
If we run the test, we find that the area should be equivalent to 16, resulting from multiplying width (8) by height (2).
Now we create a Square
class that extends from Rectangle
.
To validate that we did not break the functionality of the parent, we will run the test on an object created with the Square
class. Running the test, we find that it failed because now a square sets the width and height as the same value, making it impossible to have the area of a rectangle with different sides.
At this point, you might be wondering how to fix it, and you are probably thinking of different possibilities. The first and simplest might be to abstract the logic to a higher class, leaving the code as follows:
The principle indicates that a class should only implement the interfaces it needs, meaning it should not have to implement methods it does not use. The purpose of this principle is to force us to write small interfaces, aiming to apply the cohesion principle to each interface.
Imagine we have a business selling desktop computers, and we know that all computers should extend from the Computer class. We would have something like this:
In our business, everything is going great, and now we want to extend our product catalog a bit more, so we decide to start selling laptops. A useful attribute of a laptop is the size of the built-in screen, but as we know, this is only present in laptops and not desktop computers (generalizing). Initially, we might think an implementation could be:
The problem with this implementation is that not all classes, for example, DellDesktop
, require the methods to read and write the size of the built-in screen. Therefore, we should think about separating both logics into two interfaces, leaving our code like this:
Everything sounds perfect, but have you noticed the problem? JavaScript only supports one parent class, so the solution would be to apply a mixin. Here is the code using a mixin:
This principle establishes that dependencies should be on abstractions, not concretions. In other words, it requires classes to never depend on other classes, and all relationships should be in an abstraction. This principle has two rules:
High-level modules should not depend on low-level modules. This logic should be in an abstraction.
Abstractions should not depend on details. Details should depend on abstractions.
Imagine we have a class that allows us to send an email:
In this example, we can see that the rule is broken since the Email class depends on the service provider. What if later we want to use Yahoo instead of Gmail?
To solve this, we should remove that dependency and add it as an abstraction.
This way, we no longer care about the provider or how the provider implements email sending. The Email class only takes care of one thing, asking the provider to send an email.
That’s it for this post about SOLID principles in JavaScript. I would appreciate it if you could leave comments and suggestions on what other topics you would like us to cover.