Design patterns are incredibly useful and one of the best programming practices followed by software developers.
They provide solutions to those problems that are recurrent during software development. They are also known as GoF (gang of four) Design Patterns, and there are three types of software design patterns: Creational, behavioral, and structural.
We'll discuss one of the creational patterns called the singleton pattern.
The singleton pattern, as the name suggests, represents a single instance of a class.
To enforce this single instance rule, the class uses a private constructor. This prevents external classes from creating new instances of the class directly. The purpose of the Singleton pattern is to enforce the presence of only one instance of a class.
That single instance is responsible for coordinating actions and managing resources while providing a centralized point of control across all the components of an application.
There are many use cases where we need to use a single instance with centralized access. There are a few use cases for using a single instance:
For database connections
For configuration management
For logging management
For sharing stateful information
For optimizing resource usage
For implementing thread pools
For managing the cache
Consider a scenario where we implement the singleton pattern using the export
logger.js
, that ensures there is a single instance of the logger throughout the module and a test file main.js
to see how we can use the logger module.
Note: We will use the version 14 or later of Node.js.
// Importing the public method of Logger moduleimport { getInstance } from './logger.js'// Getting the singleton instanceconst instance1 = getInstance()const instance2 = getInstance()// Checking if both instances refer to the same object or notif (instance1 === instance2)console.log('Both instances refer to the same object')// Using the Logger to log messagesinstance1.log('I am instance 1')instance2.log('I am instance 2')// Logging entries of both instancesconsole.log(instance1.logs)console.log(instance2.logs)
Just a heads-up: logger.js
and main.js
are both in the same Code Widget. You can find them on the left side of the widget.
logger.js
:
Line 2: We declare a variable instance
and initialize it to null
to hold a single instance of the class.
Lines 5–14: The Logger
class has a constructor that initializes the logs
instance variable as an empty array. It also includes a log()
method that adds a message to the logs
array and logs it to the console.
Lines 17–23: The getInstance()
method provides access to a single instance of the class. It checks the instance
variable and, if it is null
, creates a new Logger
instance and assigns it to instance
. It then returns this instance, ensuring that all future calls to getInstance()
return the same object.
Line 26: This line exports the getInstance
method using the export
keyword.
Now let's look at the main.js
file explanation below:
main.js
:
Line 2: We import the getInstance
function from the logger.js
module using the import
keyword. The getInstance
function is the public method that provides access to the singleton instance of the Logger
class.
Lines 5-6: We call the getInstance()
method twice, creating two instances, instance1
and instance2
of the Logger
class.
Lines 9-10: We check if instance1
and instance2
refer to the same object. Since the getInstance
function ensures that there is only one instance, both instance1
and instance2
will refer to the same object. So, if this is true, which is expected due to the Singleton pattern, it logs the message to the console.
Lines 13-14: We call the log
method for both instances instance1
and instance2
, to log messages.
Lines 17-18: We print the logs
array of both instances instance1
and instance2
to the console. Since both instances share the same instance of the Logger
class, they also share the same logs
array. The console output will show the log entries of both instances.
The singleton pattern is useful when we need a single access point to a resource or service. It provides a simple and centralized solution. However, since it offers global access, it should be used with care. Overuse can affect the modularity and testability of the application.
Free Resources