Programming
JavaScript and Design patterns - Chapter 1 π
π€ INTRO
Welcome, dear coders! I am really glad that you are interested in this kind of content. Here we are, at the second chapter of the Design Patterns explained using JavaScript programming language series. If you missed the previous introductory chapter of this series, please check it out at the link below!
In this article, we will discuss another design pattern The Singleton Pattern.
π€·ββοΈ WHAT IS THE SINGLETON PATTERN?
Singleton is a creational design pattern that ensures that a class has only one instance.
Usually, the goal is to manage the global application state. To share data between the different parts of an application (components) that are not interconnected. You can find it being used as the source of config settings for a web app, on the client-side for anything initiated with an API key, and to store data in memory in a client-side web application such as Flux, Redux, or Vuex. Also, a singleton can be used as a singleton service (Like Angular does). When using it as a service, you manage that only one service instance exists in an app. Of course, those are all applications within a specific JavaScript library or platform such as Angular, React, or Vue.
β IMPLEMENTATION
Real-World application using simplified Load Balancer logic.
1// Simplified load balancer
2const loadBalancer = (function () {
3 // This is the unique load balancer instance variable
4 let loadBalancerInstance;
5
6 // load balancer available servers
7 const servers = [];
8
9 // Function that will create the load balancer (assign servers) ONLY once
10 function create() {
11 servers.push("SERVER I");
12 servers.push("SERVER II");
13 servers.push("SERVER III");
14 servers.push("SERVER IV");
15 return servers;
16 }
17
18 // Function for getting random server
19 function getServer() {
20 return Math.floor(Math.random() * loadBalancerInstance) + 1;
21 }
22
23 return {
24 // Function that will either create or not create a new load balancer instance
25 getInstance: function () {
26 // If the load balancer instance is null or undefined, the load balancer will be created!
27 if (!loadBalancerInstance) {
28 loadBalancerInstance = create();
29 } // If the load balancer is already created we just return an existing instance
30
31 return loadBalancerInstance;
32 },
33 };
34})();
35
36//trying to create the load balancer instance - Success
37const balancer_0 = loadBalancer.getInstance();
38
39// Trying to create the load balancer instance
40// - Error - Instance is already created
41// Ppreviously created instance is assigned to any of the following variables...
42const balancer_1 = loadBalancer.getInstance();
43const balancer_2 = loadBalancer.getInstance();
44
45if (balancer_0 === balancer_1 && balancer_0 === balancer_2) {
46 console.log(
47 "%c%s",
48 "color: white; background: lightgreen; font-size: 24px;",
49 "Balancers are the same instance!"
50 );
51} else {
52 console.log(
53 "%c%s",
54 "color: black; background: red; font-size: 24px;",
55 "Balancers are not the same instance!"
56 );
57}
58
π‘ The singleton is not freed until the termination of the program
π§ DISCUSSION
The Singleton design pattern is a very specific type of single instance, specifically one that is:
βοΈ Accessible via a global, static instance field
βοΈ Created either on program initialization or upon first access
βοΈ No public constructor (cannot instantiate directly)
βοΈ Never explicitly freed (implicitly freed on program termination)
It is because of this specific design choice that the pattern introduces several potential long-term problems:
π
Inability to use abstract or interface classes
π Inability to subclass
π High coupling across the application (difficult to modify)
π Difficult to test (can't fake/mock in unit tests)
π Difficult to parallelize in the case of mutable state (requires extensive locking)
β PROS
βοΈ You can be sure that a class has only a single instance
βοΈ You gain a global access point to that instance
βοΈ The singleton object is initialized only when it's requested for the first time
β CONS
π
Violates the Single Responsibility Principal
π The Singleton pattern can mask bad design, for instance, when the components of the program know too much about each other
π The pattern requires special treatment in a multithreaded environment so that multiple threads won't create a singleton object several times
π It may be difficult to unit test the client code of the Singleton because many test frameworks rely on inheritance when producing mock objects. Since the constructor of the Singleton class is private and overriding static methods is impossible in most languages, you will need to think of a creative way to mock the singleton. Or just don't write the tests. Or don't use the Singleton pattern