Design patterns in modern JavaScript development
- Transfer
The author of the material, the translation of which we publish, says that in the world of software development, “architectural design” can be called the process of constructing an application, during which they strive to make it high-quality, reliable and well amenable to support. Moreover, design patterns (patterns) allow you to operate with concepts that are approaches to solving common problems. These decisions can vary from abstract, conceptual, to very specific. Their knowledge allows developers to communicate effectively with each other.
If at least two developers in a team understand patterns, talking about solving problems facing the team becomes very productive. If only one member of the team knows about the patterns, it usually just happens to clarify what he knows to other members of the team. The purpose of this article is to interest readers with a kind of formal presentation of knowledge in the field of software development, showing them the idea of design patterns and describing several patterns that are interesting in that they find application in modern JavaScript development.
The design pattern “singleton” (also called “singleton”) cannot be called one of the most widely used patterns, but we start the conversation with him, since it is relatively easy to understand.
This pattern grows from the mathematical concept of a singleton - a singleton set, that is, a set containing only one element. For example, the set {null} is a singleton.
In the field of software development, the meaning of the “singleton” pattern comes down to the fact that we limit the number of possible instances of a certain class to one object. At the first attempt to create an object based on a class that implements this pattern, such an object is actually created. All subsequent attempts to create an instance of the class will return the object created during the first attempt to obtain an instance of the class.
Why another superhero when we have Batman?
Above is an example of a class that implements the singleton pattern.
It is clear that this pattern allows us to limit ourselves to just one “superhero” (this is obviously Batman). But why else might he need it?
Although this pattern is not without its own problems (previously it was called evil, given the fact that the singleton called pathological liars ), it, in some situations, can be very useful. One of these situations is to initialize configuration objects. In a typical application, it makes sense to keep only one instance of such an object, unless, in accordance with the features of the project, several similar objects are used in it.
The main example of using the singleton pattern in large popular frameworks is Angular services. The Angular documentation has a separate page explaining how to make a service a singleton.
The design of services in the form of singletones carries a deep meaning, since services are used as repositories of state, configuration, and allow you to organize the interaction between components. All this leads to the fact that the developer needs so that his application would not have several instances of the same service.
Consider an example. Suppose we have a simple application that is used to count the number of clicks on buttons.
Each click on any of the buttons updates the counter. The
number of clicks on the buttons must be stored in one object, which implements the following features:
If such an object were not a singleton (and each button had its own instance of such an object), the program would incorrectly calculate the number of clicks on the buttons. In addition, with this approach, it is necessary to solve the following problem: "From which particular object responsible for counting the clicks will the data displayed on the screen be taken?"
An observer pattern is a design pattern in which an object called a subject maintains a list of dependent objects called observer and automatically notifies them when their state changes, usually by calling one of their methods .
Understanding this pattern is not difficult if you find its analogy in the real world. Namely, we are talking about newspaper subscriptions.
Suppose you usually buy newspapers at a kiosk. You go there, asking if there is a fresh issue of your favorite newspaper. If what you need is not in the kiosk, then you go home, wasting time and then go to the kiosk again. If we consider this situation as applied to JavaScript, then it would be like a cyclic poll of some entity, performed until the necessary data is received from it.
After, when you finally get the newspaper you need, you can begin to what you have been striving for all this time - take a cup of coffee and unfold the newspaper. In JavaScript, this would be tantamount to calling a callback, which we were going to call after getting the desired result.
Finally, you can read the newspaper.
It would be much more reasonable to do this: subscribe to the newspaper and receive its latest issue every day. With this approach, the publisher will let you know that a fresh issue of the newspaper has been released and deliver it to you. You don’t have to go to the kiosk anymore. No more wasting time.
If you switch to JavaScript again, this means that you no longer have to wait in the loop for some result, and, having received it, call a certain function. Instead, you inform the subject that you are interested in certain events (messages), and pass the callback function to it, which should be called when the data of interest is ready. You, in this case, become an observer.
Now you will never miss your favorite morning newspaper.
The pattern in question has one nice feature: you do not have to be the only observer. If you can’t get your favorite newspaper, it will upset you. But the same thing will happen to other people who cannot buy it. That is why several observers can subscribe to one subject.
The pattern "observer" is used in many situations, but usually it should be used when you want to create a one-to-many relationship between objects, and at the same time, such objects should not be strongly connected. In addition, the system should be able to notify an unlimited number of objects about certain changes.
JavaScript applications are a great place to apply the observer pattern, since everything is event driven here, and instead of constantly referring to a certain entity to find out if an event of interest to you has occurred, it will be much better to let it notify you when the occurrence of this event (this is similar to the old saying: "Do not call us. When necessary, we will call you ourselves").
It is likely that you have already used designs that resemble the observer pattern. For example, this
It is useful to know about the existence of the “observer” pattern in the sense that this knowledge allows you to realize your own subject, or much faster than before, to figure out an existing solution using this pattern.
The basic implementation of this pattern does not have to be particularly complicated, but there are excellent libraries that implement it and are used in many projects. We are talking about the ReactiveX project , and its JavaScript version of RxJS .
The RxJS library allows not only subscribing to subjects, but also gives the programmer the ability to transform data in a variety of ways, allows you to combine many subscriptions, improves the ability to manage asynchronous operations. In addition, her capabilities are not limited to this. If you ever wanted to increase the capabilities of your programs for processing and converting data to a higher level, then you can recommend to study the RxJS library.
In addition to the “observer” pattern, the ReactiveX project can be proud of the implementation of the “iterator” pattern, which allows the subject to inform subscribers about the completion of the subscription, in essence, allowing to cancel the subscription on the initiative of the subject. In this article I’m not going to talk about the “iterator” pattern, but I can say that if you are just starting to learn design patterns, studying this pattern and thinking about how it composes with the “observer” pattern can be good exercises.
The facade pattern got its name from architecture. In architecture, a facade is usually one of the external sides of a building, usually the front side. English borrowed the word “facade” from French. We are talking about the word "façade", which, among other things, translates as "the front side of the building."
The facade of the building in architecture is the exterior of the building, hiding what is inside. Similar properties can be noted in the “facade” pattern, since it is aimed at hiding complex internal mechanisms behind a certain external interface. Its application allows the developer to work with an external API, arranged quite simply, and, at the same time, provides the ability to change the internal mechanisms hidden behind the facade, without disrupting the performance of the system.
The “facade” pattern can be used in a huge number of situations, among which we can especially note those when they try to make the code easier to understand (that is, they hide complex mechanisms behind simple APIs), and those when fragments of systems tend to make as much as possible looser connected to each other.
These objects need something from the dragon's den.
It is easy to see that the facade object (or layer with several objects) is a very useful abstraction. It is unlikely that anyone will want to run into a dragon if this can be avoided. The facade object is needed in order to provide other objects with a convenient API, and this object will cope with all the dragon tricks on its own.
Another useful feature of the “facade” pattern is that the dragon can be “remade” as desired, but this will not affect other parts of the application. Suppose you want to replace a dragon with a kitten. The kitten, like the dragon, has claws, but it is easier to feed. Change the dragon to a kitten - this means - to rewrite the code of the facade object without making changes to the dependent objects.
The facade pattern is often found in Angular. There, services are used as a means to simplify some basic logic. But this pattern is applicable not only in Angular, below you can see this.
Suppose we need to add a state management system to the application. To solve this problem, you can use various tools, among them - Redux, NgRx, Akita, MobX, Apollo, as well as - constantly emerging new tools. Why not try them all?
What is the main functionality that a state management library should provide? These are probably the following features:
It all looks not so bad.
Now, armed with the “facade” pattern, you can write facades to work with various parts of the state, providing convenient APIs that can be used in the program. For example, something like
After that, you can work with objects that implement the “facade” pattern and write code that will transform your code so that it can work with Apollo (managing state with GraphQL is a hot topic). Perhaps during the tests you will find that Apollo is not suitable for you, or that you are uncomfortable writing unit tests based on this state management system. No problem - write a new facade designed to support MobX, and try the system again.
Different systems for managing the state of the application, accessed through a single facade, can also be dragons ...
You probably noticed that when we talked about design patterns, we did not consider code examples. The fact is that a deep analysis of each pattern draws at least a separate chapter in the far from the thinnest book. By the way, since we're talking about books here and here - interesting publications, which you can look for those who want to delve into the study of patterns.
In the end, I want to say that in the development of patterns, nothing beats a search on the Internet, reading and independently testing various ideas. Even if it turns out that you will never use patterns, you will understand something new and will grow in areas that are unexpected for you.
Dear readers! What design patterns do you use?
If at least two developers in a team understand patterns, talking about solving problems facing the team becomes very productive. If only one member of the team knows about the patterns, it usually just happens to clarify what he knows to other members of the team. The purpose of this article is to interest readers with a kind of formal presentation of knowledge in the field of software development, showing them the idea of design patterns and describing several patterns that are interesting in that they find application in modern JavaScript development.
Singleton pattern
▍ General
The design pattern “singleton” (also called “singleton”) cannot be called one of the most widely used patterns, but we start the conversation with him, since it is relatively easy to understand.
This pattern grows from the mathematical concept of a singleton - a singleton set, that is, a set containing only one element. For example, the set {null} is a singleton.
In the field of software development, the meaning of the “singleton” pattern comes down to the fact that we limit the number of possible instances of a certain class to one object. At the first attempt to create an object based on a class that implements this pattern, such an object is actually created. All subsequent attempts to create an instance of the class will return the object created during the first attempt to obtain an instance of the class.
Why another superhero when we have Batman?
Above is an example of a class that implements the singleton pattern.
▍ Why is it needed?
It is clear that this pattern allows us to limit ourselves to just one “superhero” (this is obviously Batman). But why else might he need it?
Although this pattern is not without its own problems (previously it was called evil, given the fact that the singleton called pathological liars ), it, in some situations, can be very useful. One of these situations is to initialize configuration objects. In a typical application, it makes sense to keep only one instance of such an object, unless, in accordance with the features of the project, several similar objects are used in it.
▍Where to use it?
The main example of using the singleton pattern in large popular frameworks is Angular services. The Angular documentation has a separate page explaining how to make a service a singleton.
The design of services in the form of singletones carries a deep meaning, since services are used as repositories of state, configuration, and allow you to organize the interaction between components. All this leads to the fact that the developer needs so that his application would not have several instances of the same service.
Consider an example. Suppose we have a simple application that is used to count the number of clicks on buttons.
Each click on any of the buttons updates the counter. The
number of clicks on the buttons must be stored in one object, which implements the following features:
- It allows you to count clicks on buttons.
- It makes it possible to read the current value of the click counter.
If such an object were not a singleton (and each button had its own instance of such an object), the program would incorrectly calculate the number of clicks on the buttons. In addition, with this approach, it is necessary to solve the following problem: "From which particular object responsible for counting the clicks will the data displayed on the screen be taken?"
Observer Pattern
▍ General
An observer pattern is a design pattern in which an object called a subject maintains a list of dependent objects called observer and automatically notifies them when their state changes, usually by calling one of their methods .
Understanding this pattern is not difficult if you find its analogy in the real world. Namely, we are talking about newspaper subscriptions.
Suppose you usually buy newspapers at a kiosk. You go there, asking if there is a fresh issue of your favorite newspaper. If what you need is not in the kiosk, then you go home, wasting time and then go to the kiosk again. If we consider this situation as applied to JavaScript, then it would be like a cyclic poll of some entity, performed until the necessary data is received from it.
After, when you finally get the newspaper you need, you can begin to what you have been striving for all this time - take a cup of coffee and unfold the newspaper. In JavaScript, this would be tantamount to calling a callback, which we were going to call after getting the desired result.
Finally, you can read the newspaper.
It would be much more reasonable to do this: subscribe to the newspaper and receive its latest issue every day. With this approach, the publisher will let you know that a fresh issue of the newspaper has been released and deliver it to you. You don’t have to go to the kiosk anymore. No more wasting time.
If you switch to JavaScript again, this means that you no longer have to wait in the loop for some result, and, having received it, call a certain function. Instead, you inform the subject that you are interested in certain events (messages), and pass the callback function to it, which should be called when the data of interest is ready. You, in this case, become an observer.
Now you will never miss your favorite morning newspaper.
The pattern in question has one nice feature: you do not have to be the only observer. If you can’t get your favorite newspaper, it will upset you. But the same thing will happen to other people who cannot buy it. That is why several observers can subscribe to one subject.
▍ Why is it needed?
The pattern "observer" is used in many situations, but usually it should be used when you want to create a one-to-many relationship between objects, and at the same time, such objects should not be strongly connected. In addition, the system should be able to notify an unlimited number of objects about certain changes.
JavaScript applications are a great place to apply the observer pattern, since everything is event driven here, and instead of constantly referring to a certain entity to find out if an event of interest to you has occurred, it will be much better to let it notify you when the occurrence of this event (this is similar to the old saying: "Do not call us. When necessary, we will call you ourselves").
It is likely that you have already used designs that resemble the observer pattern. For example, this
addEventListener
. Adding an event listener to an element has all the signs of using the observer pattern:- You can subscribe to the object.
- You can unsubscribe from the object.
- An object can inform all its subscribers about the event.
It is useful to know about the existence of the “observer” pattern in the sense that this knowledge allows you to realize your own subject, or much faster than before, to figure out an existing solution using this pattern.
▍Where to use it?
The basic implementation of this pattern does not have to be particularly complicated, but there are excellent libraries that implement it and are used in many projects. We are talking about the ReactiveX project , and its JavaScript version of RxJS .
The RxJS library allows not only subscribing to subjects, but also gives the programmer the ability to transform data in a variety of ways, allows you to combine many subscriptions, improves the ability to manage asynchronous operations. In addition, her capabilities are not limited to this. If you ever wanted to increase the capabilities of your programs for processing and converting data to a higher level, then you can recommend to study the RxJS library.
In addition to the “observer” pattern, the ReactiveX project can be proud of the implementation of the “iterator” pattern, which allows the subject to inform subscribers about the completion of the subscription, in essence, allowing to cancel the subscription on the initiative of the subject. In this article I’m not going to talk about the “iterator” pattern, but I can say that if you are just starting to learn design patterns, studying this pattern and thinking about how it composes with the “observer” pattern can be good exercises.
Facade Pattern
▍ General
The facade pattern got its name from architecture. In architecture, a facade is usually one of the external sides of a building, usually the front side. English borrowed the word “facade” from French. We are talking about the word "façade", which, among other things, translates as "the front side of the building."
The facade of the building in architecture is the exterior of the building, hiding what is inside. Similar properties can be noted in the “facade” pattern, since it is aimed at hiding complex internal mechanisms behind a certain external interface. Its application allows the developer to work with an external API, arranged quite simply, and, at the same time, provides the ability to change the internal mechanisms hidden behind the facade, without disrupting the performance of the system.
▍ Why is it needed?
The “facade” pattern can be used in a huge number of situations, among which we can especially note those when they try to make the code easier to understand (that is, they hide complex mechanisms behind simple APIs), and those when fragments of systems tend to make as much as possible looser connected to each other.
These objects need something from the dragon's den.
It is easy to see that the facade object (or layer with several objects) is a very useful abstraction. It is unlikely that anyone will want to run into a dragon if this can be avoided. The facade object is needed in order to provide other objects with a convenient API, and this object will cope with all the dragon tricks on its own.
Another useful feature of the “facade” pattern is that the dragon can be “remade” as desired, but this will not affect other parts of the application. Suppose you want to replace a dragon with a kitten. The kitten, like the dragon, has claws, but it is easier to feed. Change the dragon to a kitten - this means - to rewrite the code of the facade object without making changes to the dependent objects.
▍Where to use it?
The facade pattern is often found in Angular. There, services are used as a means to simplify some basic logic. But this pattern is applicable not only in Angular, below you can see this.
Suppose we need to add a state management system to the application. To solve this problem, you can use various tools, among them - Redux, NgRx, Akita, MobX, Apollo, as well as - constantly emerging new tools. Why not try them all?
What is the main functionality that a state management library should provide? These are probably the following features:
- A mechanism for notifying the state management system that we need to change state.
- The mechanism for obtaining the current state or its fragment.
It all looks not so bad.
Now, armed with the “facade” pattern, you can write facades to work with various parts of the state, providing convenient APIs that can be used in the program. For example, something like
facade.startSpinner()
, facade.stopSpinner()
and facade.getSpinnerState()
. Such methods are easy to understand, you can easily refer to them in a conversation about the program.After that, you can work with objects that implement the “facade” pattern and write code that will transform your code so that it can work with Apollo (managing state with GraphQL is a hot topic). Perhaps during the tests you will find that Apollo is not suitable for you, or that you are uncomfortable writing unit tests based on this state management system. No problem - write a new facade designed to support MobX, and try the system again.
Different systems for managing the state of the application, accessed through a single facade, can also be dragons ...
Summary
You probably noticed that when we talked about design patterns, we did not consider code examples. The fact is that a deep analysis of each pattern draws at least a separate chapter in the far from the thinnest book. By the way, since we're talking about books here and here - interesting publications, which you can look for those who want to delve into the study of patterns.
In the end, I want to say that in the development of patterns, nothing beats a search on the Internet, reading and independently testing various ideas. Even if it turns out that you will never use patterns, you will understand something new and will grow in areas that are unexpected for you.
Dear readers! What design patterns do you use?