JavaScript and Design patterns - Chapter 1 πŸš€

Cover Image for JavaScript and Design patterns - Chapter 1 πŸš€
Lazar Stankovic
Lazar Stankovic

πŸ€“ 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


πŸ™ THANK YOU FOR READING!