skip to Main Content

According to the design rules, a singleton pattern must exist in an instance. If I remove the line of code and return an object, will the function be a single pattern?

const singleton = (function(){
    
    let instance, 
         counter = 0;
    
    const createInstance = () => ({
        getCounter: () => counter,
        addCounter: (num) => counter += num,
    })
    
    
    return {                                    
        getInsctance: () => createInstance()  //  insctance || (insctance = createInsctance())  ????
    } 
    
})()


const r = singleton.getInsctance()
const r2 = singleton.getInsctance()

r.addCounter(12) 
r2.addCounter(32) 

singleton.counter = 100 

console.log(r2.getCounter()) // 44
console.log(r.getCounter())  // 44

Why is this feature not singleton pattern?

2

Answers


  1. The definition of the Singleton design pattern is that there will only be one instance of the class. Whenever you call the constructor, it should return the same instance. This is typically implemented by creating a new instance the first time the constructor is called, and saving that instance in a variable so that future calls can return it.

    In your code this is what the instance variable is for. The first time you call singleton.getInstance(), the variable is undefined, so the || logical operator returns the value of the second expression. This calls createInstance(), which creates a new object. It saves this in instance and returns it.

    Your code acts like a singleton because you’re not actually storing any data in the instances. You’re keeping the counter value in a closure variable that’s shared by all instances.

    To make a true singleton, you should move counter into a property of the object that’s returned by createInstance(), and use this.counter in the other methods. Note that for this to work the methods must be traditional functions rather than arrow functions, because arrow functions don’t receive this from the method call.

    The next time you call singleton.getInstance(), the variable instance contains that object. So the logical || operator returns that value.

    Assigning to singleton.counter has no effect in either version of the code. In your version, counter is a local variable; this is not the same as a property of the object that the IIFE returns. In my version, counter is a property of the instance returned by createInstance(), which is not the same as singleton itself.

    const singleton = (function() {
    
      let instance;
    
      const createInstance = () => ({
        counter: 0,
        getCounter () {
          return this.counter;
        },
        addCounter (num) {
          this.counter += num;
        },
      })
    
      return {
        getInstance: () => instance || (instance = createInstance())
      }
    })()
    
    const r = singleton.getInstance()
    const r2 = singleton.getInstance()
    
    r.addCounter(12)
    r2.addCounter(32)
    
    singleton.counter = 100
    
    console.log(r2.getCounter()) // 44
    console.log(r.getCounter()) // 44

    Note that counters are not a good use of the Singleton pattern. Usually you need to create multiple counters, each keeping track of their own sequence of values.

    Login or Signup to reply.
  2. Singleton is a pattern that means that the class/function cannot be a singleton unless there cannot be two instances of it at the same time.

    A nice implementation of the singleton pattern can be seen here https://www.dofactory.com/javascript/design-patterns/singleton

    var Singleton = (function () {
        var instance;
    
        function createInstance() {
            var object = new Object("I am the instance");
            return object;
        }
    
        return {
            getInstance: function () {
                if (!instance) {
                    instance = createInstance();
                }
                return instance;
            }
        };
    })();
    
    function run() {
    
        var instance1 = Singleton.getInstance();
        var instance2 = Singleton.getInstance();
    
        console.log("Same instance? " + (instance1 === instance2));
    }
    
    run();

    From the outside you cannot call createInstance, that is, to protect the pattern and you can call getInstance.

    Since the requirement of the singleton pattern is to not have more than one instance, you can have 0 instance and you can destroy an object and create another. But you will need to make sure that at all times there will be no more than one instance of that class/function.

    Your code is not following the singleton pattern, because, from the outside it can be instantiated even when it’s already instantiated:

    const singleton = (function(){
        
        let instance, 
             counter = 0;
        
        const createInstance = () => ({
            getCounter: () => counter,
            addCounter: (num) => counter += num,
        })
        
        
        return {                                    
            getInsctance: () => createInstance()  //  insctance || (insctance = createInsctance())  ????
        } 
        
    })()
    
    
    const r = singleton.getInsctance()
    const r2 = singleton.getInsctance()
    
    alert((r === r2) ? "it is singleton" : "it is not a singleton")

    Let’s make it a singleton:

    const singleton = (function(){
        
        let instance, 
             counter = 0;
        
        const createInstance = () => ({
            getCounter: () => counter,
            addCounter: (num) => counter += num,
        })
        
        
        return {                                    
            getInsctance: () => instance ? instance : (instance = createInstance())  //  insctance || (insctance = createInsctance())  ????
        } 
        
    })()
    
    
    const r = singleton.getInsctance()
    const r2 = singleton.getInsctance()
    
    alert((r === r2) ? "it is singleton" : "it is not a singleton")

    Here I’m reusing the instance instead of creating it if it was already existent.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search