addyosmani / essential-js-design-patterns

Repo for my 'Learning JavaScript Design Patterns' book
http://addyosmani.com/resources/essentialjsdesignpatterns/book/
4.82k stars 792 forks source link

Incorrect sample used for Singleton? #39

Closed andrzejpolis closed 12 years ago

andrzejpolis commented 12 years ago

Wikipedia states:

In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object.

So below code from your book is misleading:

var mySingleton = function () {

    // here are our private methods and variables
    var privateVariable = "something private";

    function showPrivate() {
      console.log( privateVariable );
    }

    // public variables and methods (which can access 
    // private variables and methods )
    return {

      publicMethod: function () {
        showPrivate();
      },

      publicVar: "the public can see this!"

    };
  };

// Usage:

// Create a new singleton
var single = mySingleton();

// Execute our public method which accesses
// a private one. 
// Outputs: "something private"
single.publicMethod(); 

// Outputs: "the public can see this!"
console.log( single.publicVar ); 

With such code you can easily create two instances. For example this code results in unacceptable behavior in singleton pattern:

var single = mySingleton();
var another = mySingleton();

// Outputs: "the public can see this!"
console.log( single.publicVar ); 
// Outputs: "the public can see this!"
console.log( another.publicVar ); 

// this should affect singleton object accessible by both variables
another.publicVar  = 'changed'

// but...
// Outputs: "the public can see this!"
console.log( single.publicVar ); 
// Outputs: "changed"
console.log( another.publicVar ); 
addyosmani commented 12 years ago

Here is an alternative Singleton implementation. (I'll double check later, but I have a feeling the OP sample you posted was a module pattern variant that shouldn't be in the singleton section at all). Will let you know.

(function(global) {
  "use strict";
  var MySingletonClass = function() {

    if ( MySingletonClass.prototype._singletonInstance ) {
      return MySingletonClass.prototype._singletonInstance;
    }
    MySingletonClass.prototype._singletonInstance = this;

    this.Foo = function() {
      // ...
    };
  };

var a = new MySingletonClass();
var b = MySingletonClass();
global.result = a === b;

}(window));

console.log(result);

That said, keep in mind that even with this, you could create a new instance of MySingletonClass as var c = new MySingletonClass(), which wouldn't be equivalent in the above sample.

karolk commented 12 years ago

Not sure why not just use an object literal as a singleton (possible exposed from a closure to have private properties). Prototype is for memory efficiency when there are multiple instances, but since we want 1 instance always, prototype only gets in a way

(function(host) {

    var privateMethod = function() {
        //do magic
    }

    var Single = {
        publicMethod: function() {
            //call private
            privateMethod(1);
        }
    }

    host.Single = Single

})(this);
addyosmani commented 12 years ago

Alternative, which I find just as readable:

(function(){
    var instance;
    var counter = 0;
    var Something = function() {

        if (instance) {
            return instance;
        }

        this.counter = counter++;
        return instance = this;
    };

    var a = new Something();
    var b = new Something();
    var c = new Something();

    console.log(a.counter, b.counter, c.counter);
    console.log(a === b, a === c);

})()​

Side-note: It looks like something didn't make its way over to the Singleton pattern section in between moving content from branches. The OP sample you pointed out looks much more like the module pattern and I completely agree that it should be replaced. What are your thoughts on the above?

andrzejpolis commented 12 years ago

Next example in your book shows very reasonable implementation of Singleton:

var Singleton = (function () {
  var instantiated;

  function init() {

    // singleton 

    return {
      publicMethod: function () {
        console.log( "Hey Alex!" );
      },

      publicProperty: "test"
    };
  }

  return {
    getInstance: function () {
      if ( !instantiated ) {
        instantiated = init();
      }
      return instantiated;
    }
  };
})();

And this one is proper. You cannot obtain two objects with implementation of 'publicMethod' function and what is more important with two instances of 'publicProperty'.

I propose to remove first implementation.

In fact for me even stating that something like:


var mySingleton = {

  property1: "Foo",

  property2: "Bar",

  method1: function () {
    console.log( "Hey Nicholas!" );
  }

};

... is singleton is wrong. Singleton pattern is something that allows us to get always same object when using class (or type). You don't have any class or type here. So it's not singleton. It's single object :)

addyosmani commented 12 years ago

I agree with the removal of the first implementation and might also include the variation in my most recent comment as well. Thank you so much for flagging this!. I'll keep the issue open a little longer in case there are other variations that come through, but for now, I'm happy to update the book based on what we've discussed so far.

millermedeiros commented 12 years ago

Singletons differ from static "classes" since you can delay the initialization (usually because the singleton requires some info that might not be available during "initialization" time), an object literal isn't a singleton. Here is a true JavaScript singleton:

var mySingleton = (function () {

    // instance is used to store a reference to the singleton
    var instance;

    // here are our private methods and variables
    var privateVariable = "something private";

    function showPrivate() {
      console.log( privateVariable );
    }

    // create a new singleton instance
    // useful for cases when the module initialization should be deferred
    // (this is a naive example since this code could be static)
    function create(){
        var api = {
            publicMethod: function () {
                showPrivate();
            },
            publicVar: "the public can see this!"
        };
        return api;
    }

    // convert mySingleton into a function that creates a single instance
    return function(){
        if (instance == null) {
            instance = create();
        }
        return instance;
    };

  }());

// Usage:
var single = mySingleton();

// Execute our public method which accesses
// a private one.
// Outputs: "something private"
single.publicMethod();

// Outputs: "the public can see this!"
console.log( single.publicVar );

// it will always return the same instance
var a = mySingleton();
var b = mySingleton();
console.log( a === b ); // true

TBH, I never needed a true singleton in JavaScript and every time you do find yourself needing it you should reconsider your design. Singletons are red flags in most cases. It is usually a sign that your modules are tightly coupled and/or that you have state logic scattered across multiple parts of the code (otherwise it wouldn't matter if the object was static and/or if you had multiple instances). Singletons are usually harder to test (hidden dependencies, order might affect results, hard to mock/stub dependencies, can't create multiple instances, etc...).

More info about when to use singletons and its problems: http://www.ibm.com/developerworks/webservices/library/co-single/index.html and this "response" to one of the comments is also really good and shows how singletons can increase tight coupling: http://misko.hevery.com/2008/10/21/dependency-injection-myth-reference-passing/

addyosmani commented 12 years ago

@millermedeiros Thanks for sharing your take on the pattern. In the context of the book, the Singleton is really there just to demonstrate how the pattern can/should be implemented in JS and similarly, I haven't found myself needing it for anything serious. I wanted to include a comprehensive 'fuller' Singleton example as well as a (valid) variation implementing it using the fewest lines of code.

Unless @petermichaux feels differently about the pattern, I think I'll adjust the fuller example to better fit in line with yours.

karolk commented 12 years ago

@addyosmani I like your updated Singleton pattern. In JS the advantage of instantiating is that instanceof and instance.constructor will point at the right constructor function. Keeping the closure around provides private methods. Otherwise I am not sure if they can be distinguished at runtime. I guess property lookup via prototype will be a bit slower but this is negligible.

On top of that, I just realised that this discussion completely ignores ES5. Using Object.getPrototypeOf and Object.create I can create another object inheriting from hidden prototype. So I guess the only reliable method is to use module pattern.

var singleton = (function(host){
    var instance;
    var counter = 0;
    var Something = function() {

        if (instance) {
            return instance;
        }

        this.counter = counter++;
        return instance = this;
    };

    Something.prototype.special = 1

    return new Something();
})()

var copy = Object.create( Object.getPrototypeOf(singleton) );

console.log( copy.special );
mxriverlynn commented 12 years ago

It is rather subjective and difficult to lock down a 'true javascript singleton' as the use of singletons was originally intended to solve a specific set of problems in static languages like C++, Java and C#. That being said, I'm going to toss in a potentially controversial opinion: We can't have true singletons in JavaScript.

The factory patterns of static languages like C++, C# and Java, used to create singleton objects, are not what define a singleton. Design patterns are defined by their intent. The problem they solve. The reason the pattern exists, how the pattern is used and why. The implementation of a pattern is nearly irrelevant and is very convoluted, as can be demonstrated by both the Proxy and Decorator patterns being implemented with the same code while having two very distinct purposes for existence and use.

While we can generally provide a singleton-like object - and very easily do so - JavaScript provides a distinct set of challenges because of the intent of the singleton pattern.

I think it's safe to say that the intent of a singleton is to prevent multiple instances of the object from existing, so that all access to the object, it's data and methods, happens within the single instance. This is generally done through one globally accessible object or factory that returns the singleton. And nearly all definitions of the singleton pattern get at this very quickly in their description of what a singleton is, and how it's used.

In JavaScript, this can be facilitated in a number of ways through the use of object literals, factory methods, factory objects, and more. But we run in to a problem with JavaScript because of the very nature of JavaScript's prototypal inheritance, dynamic objects, and loose type system.

The most basic singleton is an object literal: var singleton = {}. This is a singleton by definition because you can't create new instances of it. There are other ways to create singletons, such as factory methods and IIFEs that have been shown in previous comments here. The purpose of any of these is to create a single object, and never create more. On the surface, they provide what looks like a singleton. But in truth, these are only single instances of objects and not a singleton.

The problem is prototypal inheritance.

In static languages like C++ where the singleton was first defined, you could not inherit directly from objects. You could only inherit from class definitions. This meant that a properly constructed singleton would truly be a singleton - no possible way of creating a new instance of that class.

In JavaScript, though, we can get around this limitation with prototypes, very easily:

var singleton = {};

var a = Object.create(singleton);

function B = {};
B.prototype = singleton;

c = new B();

Technically, we still have a singleton in the object, singleton. But we've violated the intent and purpose of the singleton pattern by inheriting directly from it with objects a and c.

We're allowing multiple object instances to provide the API (data and methods) of the singleton object. We can override the data and methods. We can modify the new instances to look and act like the original singleton (which they do because of prototypal inheritance) but provide instance variables and instance methods that no longer act directly on the singleton. We can even replace the original instance that was supposed to be shared across all our app with our new instances and allow for new instances to be created from it (though this last point can be mitigated with Object.freeze and the like).

The end result is that there is no "true singleton" in JavaScript. There are single instance objects. We can use factory methods, immediate functions, object literals and other techniques to create single instance objects. But we can never truly have a singleton pattern implementation because we can easily violate the intent of the singleton pattern by inheriting from it and providing multiple instances of the intended API through prototypal inheritance.

addyosmani commented 12 years ago

@derickbailey I don't think that's controversial at all. You touched upon some very important points, namely that as hard as we might try to fit this pattern into JavaScript, its very likely a mis-match for the language. I'd love to see references to where someone has correctly used this pattern to solve anything more than an academic-level simulation of it in JS.

I think I'll update the section to strongly stress that singletons, whilst somewhat implementable, can't be fully implemented in JS in case we run into cases of developers actively using it thinking that its capabilities are 1:1 with languages like C++, Java and so on.

petermichaux commented 12 years ago

I agree that the example in the original report is not the singleton pattern at all.


The example in the first comment from Addy is not the singleton pattern either because it does not enforce the creation of only one element. The reason is that the singleton is stored with the same level of accessibility as the constructor function.

var MySingletonClass = function() {

    if ( MySingletonClass.prototype._singletonInstance ) {
      return MySingletonClass.prototype._singletonInstance;
    }
    MySingletonClass.prototype._singletonInstance = this;

    this.Foo = function() {
      // ...
    };
  };

var a = new MySingletonClass();
// the next line breaks the enforcing of a single instance
MySingletonClass.prototype._singletonInstance = null;
var b = new MySingletonClass();

I think there is also a problem with this part of the comment

That said, keep in mind that even with this, you could create a new instance of MySingletonClass as var c = new MySingletonClass(), which wouldn't be equivalent in the above sample.

When a constructor function returns an object explicitly that is the result of the new expression rather than the this object we normally use. See ECMAScript 3rd edition section 13.2.2 steps 7 and 8.

var a = {};
var b;
function Alpha() {
    b = this;
    return a;
}
alert(new Alpha() === a); // true

The example in the first comment from karolk misses a part of the usual singleton pattern that is considered important which is lazy instantiation. If the creation of the singleton is expensive and the singleton is never used by the program (perhaps only used under certain conditions) then this upfront/eager creation is wasteful an causes startup slowdown.


In the second comment from Addy there is a problem in that the instance variable is not hidden from the the location where the constructor is called. That would mean something could mutate the value of the instance variable between calls to the constructor.

I think this is more clear.

var Something;
(function(){
    var instance;
    var counter = 0;
    Something = function() {

        if (instance) {
            return instance;
        }

        this.counter = counter++;
        return instance = this;
    };
}());​

var a = new Something();
var b = new Something();
var c = new Something();

console.log(a.counter, b.counter, c.counter);
console.log(a === b, a === c);

But does this enforce a single instance? Adding an increment method?

var Something;
(function() {
    var instance;
    var counter = 0;
    Something = function() {

        if (instance) {
            return instance;
        }

        this.counter = counter++;
        return instance = this;
    };
    Something.prototype.increment = function() {
        this.counter++;
    };
}());

var a = new Something();
var b = Object.create(a);
b.increment();
console.log(a.counter, b.counter); // 0, 1
console.log(a instanceof Something); // true
console.log(b instanceof Something); // true

It's hard to say if the a object is a singleton in this case or not since there are two instances of Something with different state.


I think the example in the first comment from andrzejpolis is a candidate for the singleton pattern. Note the example doesn't use this which is important to avoid the problem with Object.create noted above.

The example in the first comment from millermedeiros seems very similar.


In the second comment from karolk the instantiation is not lazy.

millermedeiros commented 12 years ago

@derickbailey as I said on my previous comment, static objects aren't singletons. The fact that you can use Object.create() to extend it or set it as the prototype of another class doesn't invalidate the pattern. From the Gang of Four book:

Intent: Ensure a class only has one instance, and provide a global point of access to it. Applicability:

  • there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point.
  • when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code.

What makes the singleton (anti)pattern is the global access to the instance (usually through MySingleton.getInstance()) and the private constructor - you don't call new MySingleton() directly, although in JS that is possible as pointed in some of the examples above.

The second bullet on "applicability" references a case where you might need code like this:

mySingleton.getInstance = function(){
  if (this._instance == null) {
    if ( isFoo() ) {
       this._instance = new FooSingleton();
    } else {
       this._instance = new BasicSingleton();
    }
  }
  return this._instance;
};

So the getInstance() becomes kind of a Factory Method and you don't need to update everywhere on your code that access it, FooSingleton in the example above would be a subclass of BasicSingleton and implement the same interface.

Why the defer of execution is important for a singleton:

In C++ it also serves to isolate from the unpredictability of the order of dynamic initialization, returning control to the programmer. ... Note the distinction between a simple static instance of a class and a singleton: although a singleton can be implemented as a static instance, it can also be lazily constructed, requiring no memory or resources until needed. Another notable difference is that static member classes cannot implement an interface, unless that interface is simply a marker. So if the class has to realize a contract expressed by an interface, it really has to be a singleton. wikipedia

If you have a static object that is initialized directly you need to make sure the code is always executed in the same order (in case objFoo needs objBar during its initialization) and that isn't scalable specially when you have hundreds of source files.

Singletons and static objects are indeed useful but we shouldn't overuse them, the same way that we shouldn't overuse the other patterns. - Understand the problems you are trying to solve then pick the best tool for the job, as John Carmack once said:

"Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function." - twitter

PS: I'm against the overload of the new keyword to always return a single instance since it is misleading. PPS: JavaScript flexibility makes some design patterns obsolete but the "lack" of certain features and some "flaws" also create new ones.

addyosmani commented 12 years ago

Fixed by the latest set of commits on the 1.5.2 branch. Thanks everyone!