Design Patterns meet the Frontend
As an Angular developer I work with Singletons every day, in the shape of
Injectables. However, when I sat down to write about Singletons I did a double take.
NOTE: Code samples in this article will be using TypeScript (3.7) and two examples at the end will use Java.
Let’s break this down into a few questions:
- 🤔 What constitutes a Singleton?
- ❓ Why are they used in Angular?
- 😍 What advantages do we get from Singletons?
- 📣 Why is there a debate over whether they are an Anti-Pattern or not?
In this article, I’ll attempt to shed light on some of these questions.
🤔 What constitutes a Singleton?
We’ll start with the definition of a Singleton:
The restriction of the instantiation of a class to a single instance.
In other words, any time we instantiate a class, we’ll only ever get the first actual instance of that class.
These Singletons can implement interfaces, be passed around methods as arguments and can be polymorphic.
However, every time we use the class, it will be the same object that we first created. The code sample below may shine more light on the situation:
And that’s it! Creating a Singleton is as simple as that! 🔥🔥🔥
But what exactly does that mean? How will this affect our programming and where do we find value in this?
Well, a Singleton can be used to store shared state. You can instantiate your Singleton in your app, change some data within it and it will be reflected across your app.
⚠️ Doing this could be dangerous if you don’t think about it carefully!
Let’s modify our sample above to illustrate this:
The outcome of the above sample is:
Hi, my name is John
Hi, my name is John
🤔🤔 But didn’t we set
myService.user to have a name of
We did, but that was before we set
differentService.user to have a name of
By doing so, as there can only ever be one instance of the
UserService, we actually changed the name of the single
user object from
This idea of being able to share state can be very useful at times, for example, if you want to keep track of the current logged in user.
Singletons can be lazy loaded, potentially enhancing the startup performance of an app, however, on multi-threaded languages, implementation of a lazy loaded Singleton can cause issues of multiple instantiations across multiple threads, breaking the pattern, if not declared to be synchronized across threads.
❓ So, why are they used in Angular?
Angular’s Dependency Injection relies on
Injectables. Dependency Injection itself is also a Design Pattern, but it is relevant here as in this context, it makes use of the Singleton Pattern.
Angular apps consist usually with multiple modules (
NgModules), but at their minimum have one root module which has a list of required for the app. These are then injected into classes that require them.
Let’s take a look at an Angular Component that seeks a
LogService to be injected into it.
The first thing to take from this, is that the
constructor of our component takes an argument of type
Secondly, without creating an instance of this class, we use it in our
ngOnInit() method. That is because Angular will instantiate this class, hold it in memory, and pass it into any class that requires it.
By doing so, Angular is also utilizing the Singleton Pattern, as it only ever instantiates a module’s providers once, and passes the reference to this instance into any class depending on it.
😍 What advantages do we get from Singletons?
As mentioned before, we can share state with different areas within our app by using the Singleton Pattern, and in Angular, it is made much simpler thanks to their Dependency Injection system.
There are use-cases involving single access to a data store, keeping track of active items in a shopping cart, a single source of truth etc.
⚠️ Be advised that Singletons are not intended to replace any State Management system you are currently using.
However, it also allows us to create logic services, a class of methods that can be reused in multiple situations to provide the same logic, that can be instantiated once only, helping to aid code reusability and maintainability without having a high impact on memory usage or performance of apps.
Use cases for these are data services which call API Endpoints, services that will make and return decisions based on arguments passed to their methods etc.
📣 Why is there a debate over whether they are an Anti-Pattern or not?
There is an argument that Singletons promote bad design within code by actively working against the dependency inversion (DI) principle. Classes that do accept interfaces as arguments in their constructor, can simply accept a constant/final instantiated object as a parameter.
This argument generally arises from Developers who work with a strongly-typed language or when they have complete control over their DI system. See the Java example below for more clarity:
As we can see from above, the developer has complete control over the structure of the app and can decide for themselves which implementations of the interfaces they would like to use, can instatiate them at the start-up of their app, and pass them into the dependent classes.
If the Singleton Pattern was to be used here, only one possible implementation of the data service could be used. See the changes in the code below:
Whilst the code above is arguably smaller, it does break the Dependency Inversion principle and creates tighter coupling between the classes that rely on the Singleton and the Singleton itself.
It is worth noting that Angular gets around this issue with the use of Injection Tokens which is a slightly more advanced topic. It allows you to provide concrete implementations at NgModule level rather than coupling your components and services to your Singletons.
Hopefully you learned a bit ( more?) about Singletons from this article, some of their use-cases and their implementation in Angular.
If you have any questions, feel free to ask below or reach out to me on Twitter: @FerryColum.
Originally published at https://dev.to on November 27, 2019.