Responsive Advertisement

JavaScript #11: Singleton design pattern in JavaScript



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!

Đăng nhận xét

0 Nhận xét