Welcome back to series about design patterns. This is an article about Singleton design pattern.
If you don’t
read the article about factory design pattern. This is a link for you. Factory design pattern.
Table of
content
· What is singleton design pattern?
· Structure about singleton design
pattern and implementation
· Practical application
Let’s go to
the content
What is
singleton design pattern?
Singleton
design pattern is a creational pattern. This limits the number of instances of
an object to at most one. And restricts the instantiation of a class to one
“single” instance. Useful when exactly one object is needed to coordinate
action across the system.
Structure
about singleton design pattern and implementation
One main
process to manage program (can more).
Make the
default constructor private to prevent other objects from using the new
operator with the Singleton class.
Create a
static creation method that acts as a constructor. Under the hood, this method
calls the private constructor to create an object and saves it in a static
field. All following calls to this method return the cached object.
Pratical
application
Go through
the long definition. We go to the pratical task. You have a system about a
shopping online. And you need to implement a cart to clients use this. One cart
for one customer, and cart doesn’t repeat itself.
At the usual
code, how to use implement this cart?
function Cart() {
let cart = []
return {
getCart: function () {
return cart
},
addItem: function (item) {
return cart.push(item)
},
removeItem: function (index) {
return cart.splice(index, 1)
}
}
}
const cart = new Cart()
cart.addItem('Book')
cart.addItem('Note book')
cart.addItem('Pen')
cart.getCart().forEach(item => {
console.log(item)
});
/**
* Book
* Note book
* Pen
*/
It seems so
nice! Your system can manage items and like the normal cart. Wait! Did you forget
something? One customer one cart in a
website and now you try to create new cart and see what will happen.
const cart2 = new Cart()
cart2.addItem('Pencil')
cart2.getCart().forEach(item => {
console.log(item)
})
// => Pencil
Wow! It’s
wrong. As required, when creating a shopping cart anywhere, the same original
cart must be returned. So let's try to apply the Singleton design pattern.
First, we
will create the Singleton design pattern according to the function expression.
We will create a cart that can be called anywhere. And especially always return
the same cart.
const CartManager = (function () {
function CartManager() {
this.cart = []
}
let cManager
function createCartManager() {
cManager = new CartManager()
return cManager
}
return {
getCartManager: function () {
if (!cManager) {
cManager = createCartManager()
}
return cManager
}
}
})()
We have a function CartManager() that will be called and return a method called getCartManager() so that the user can call when he wants to use the cart.
Now we're
going to call the cart and add the products to see how it goes?
const cart = CartManager.getCartManager().cart
cart.push('Pen')
cart.push('Pencil')
cart.push('Book')
console.log(cart)
// => [ "Pen",
"Pencil", "Book" ]
Awesome! The
call to the cart and use is successful. Next, in case we forget to create a
shopping cart or not? We will create another shopping cart so we can see the results.
const anotherCart = CartManager.getCartManager().cart
anotherCart.push('Notebook')
console.log(anotherCart)
// => [ "Pen",
"Pencil", "Book", "Notebook" ]
console.log(cart)
// => [ "Pen",
"Pencil", "Book", "Notebook" ]
This is what
we wanted from the very beginning. A shopping cart can store products. Can be
called anywhere and in case we call create another cart, it will still return
the original cart.
The work of
adding products to the cart has been quite successful, we will come to delete
products in the cart to see how the effect will be.?
anotherCart.splice(1, 1)
console.log(anotherCart)
// => [ "Pen", "Book",
"Notebook" ]
console.log(cart)
// => [ "Pen", "Book",
"Notebook" ]
Wait! You
still find it quite complex in terms of purely array processing. Try building
methods to do these things!
const CartManager = (function () {
function CartManager() {
this.cart = []
}
let cManager
function createCartManager() {
cManager = new CartManager()
return cManager
}
return {
getCartManager: function () {
if (!cManager) {
cManager = createCartManager()
}
return cManager
},
addItem: function (item) {
cManager.cart.push(item)
},
removeItem: function (index) {
cManager.cart.splice(index, 1)
}
}
})()
We have
added two methods addItem() and removeItem() for added convenience. Try to
experience how the new way of working will be like? Note that cManager is only
a part to manage the cart, so the cart is still the main state to store data.
const cartManager = CartManager
const cart = cartManager.getCartManager().cart
cartManager.addItem('Book')
cartManager.addItem('Pen')
cartManager.addItem('Pencil')
console.log(cart)
// => [ "Book", "Pen",
"Pencil" ]
Now, we will
create another cart so we can experiment.
const anotherCart = cartManager.getCartManager().cart
cartManager.addItem('Notebook')
console.log(cart)
// => [ "Book", "Pen",
"Pencil", "Notebook" ]
console.log(anotherCart)
// => [ "Book", "Pen",
"Pencil", "Notebook" ]
You can see
that getting cartManger to use the methods also partly ensures the consistency
that we require from the beginning.
We have experienced
creating Singleton design pattern through functions. Now we will try to create
with class in JavaScript. To see how things go.
class CartManager {
constructor() {
if (CartManager.instance == null) {
this.cart = []
CartManager.instance = this
}
return CartManager.instance
}
getCart() {
return this.cart
}
addItem(item) {
return this.cart.push(item)
}
}
We will
create a class named CartManager in the constructor of this class, we set the
condition as follows. If this class has not been initialized we will return a
new array cart. If it was created before, we will return the original cart
created.
Let's use it
to see if it meets our requirements. First we will create a new cart and add a
product to it.
const cart = new CartManager()
cart.addItem('Book')
console.log(cart.getCart())
// => [ "Book" ]
So, the cart
worked. Now we will create another cart and add another product to see the
result?
const anotherCart = new CartManager()
anotherCart.addItem('Handbook')
console.log(cart.getCart()) // => [
"Book", "Handbook" ]
console.log(anotherCart.getCart()) // => [
"Book", "Handbook" ]
Awesome! Everything
was working fine. So, we have two ways to apply the Singleton design pattern.
Pros:
Ensure that
a class only has one instance.
Easily
access the sole instance of a class.
Control its
instantiation.
Retrict the
number of instances.
Access a
global variable.
Hide the
contructor of the class.
Define a
public static operation (getInstance()) that returns the sole instance of the
class.
Cons:
Can accessed
anywhere, but developers don’t know “the inner working of code to properly use
it”
Difficult to
test because of carring global static for the duration of program.
Conclusion:
We have
learned about how to implement the Singleton design pattern in JavaScript in
two ways, function and class.
If you have
any ideas feel free to comment below. Thank you for joining with me. Having a
good day!
0 Nhận xét