Hello, guys. This is the second article about SOLID principles with Javascript examples. In my previous article, I described what are patterns in general, what is SOLID, and why we should use them. If you haven't read it, pls, read it now and continue to read the current article after the previous one.
"O" - Open-closed principle. This principle says: Entities (classes, modules, methods, files, etc.) should be open for extension, but closed for modification.
From this definition is hard to understand what is going on, but let’s see a few examples.
Imagine, the next situation: we have different shapes: circles, squares, triangles. And we should calculate all areas. How can we solve it?
Of course, there are many ways how can we do it, but let’s consider the next solution:
Let’s create classes for each shape. Nothing hard, just class with different size fields like size, width, height, radius, and type field. We will use the type field when we will calculate the area of each shape.
Let's create a function, which will calculate the sum of all areas, and after that, we will call this function:
So, what do we have and where is the problem? Pls, take a look getTotalAreas function. From the first point of view, it seems that everything is ok.
But, imagine, if we need to add another shape (circle, ellipse, rhombus) what should we do? We need to create a new class for each of them, define the type and add if/else condition to the getTotalAreas method.
So, what problem you may ask.
Remember:
O - Open-closed principle. Let's repeat once more:
This principle says: Entities (classes, modules, methods, etc.) should be open for extension, but closed for modification.
In our getTotalAreas, we should make modifications each time, when we want to add a new shape.
How can we fix it and make it correctly from the open-closed principle point of view?
Let's make getArea method in each class. See the example below. Pls, pay attention that now, "type" field was removed because we don't need it more.
And let's see, how getTotalAreas function was changed:
Now, we follow the open-closed principle. Why? Because, when we will add another shape, for example, triangle, we will create a new Triangle class (open for extension), define getArea there and that's all. We don't need to change the getTotalAreas function (closed for modification). We need just put new arguments to the array during getTotalAreas call.
Now, let's look at another example, more practical. Imagine your client accepting validation error messages in a specific format. Just, for example:
And now, imagine, on the server-side, we use different services for validation, it could be our own validation checks and it could be external services that can return errors in different formats.
Let me make mock errors, of course, it can be an async request to different external services, but for our purpose let's keep examples as easy as we can.
And now, if you remember our format, which the client accept, let's transform all our errors, to the needed format:
The result will be as our client expected:
So, the problem is the same, we don't follow the open-closed principle. When we need to add a new validation from an external service, we should add new logic (if/else) to getErrors function, we do the modification.
Remember?
The Open-close principle says: Entities (classes, modules, methods, etc.) should be open for extension, but closed for modification.
How can we fix it?
One of the possible solutions we can create some general Validation error class. After, we can define some general logic there. Then, we can create for each error, own class (FacebookValidationError, GoogleValidationError).
In each class, we can specify methods, like getErrors, or transformErrors, so each validationError class should follow this rule.
And then, we will use this validation errors class in our mock function. Of course, now, we can change our function getErrors:
Pay attention, in getErrors function, now, I accept errorsList as params, not hardcoded array inside the function.
The result will be the same, but now, we follow the open-closed principle. When we want to add a new error, we will create a new validation class (open for extension) for this error and specify getErrors method, which will convert returned format from the external service, for the correct format. And then, we can use it in our getErrors general function, without any changes of this function (closed for modification).
I really hope that you enjoyed this article and if you aren't familiar with SOLID and the second principle, this article helps you to understand why is it important and how you can use it with Javascript.
I'll be waiting for you in my next article, about the third SOLID principle.