Cjxcz Odjcayrwl
Cjxcz Odjcayrwl

Reputation: 22867

Switching between singleton and prototype scope using RequireJS

Spring has very useful option, that whey I define a bean, I define a scope. If it's singleton, only one instance is created. By prototype, each time a bean is required, a new instance is created.

RequireJS provides by default singletons, so with such simple module:

Singleton.js

define([], function() {
    console.log('Instance  initialization')
    var items = []
    var singleton =  {
      getItems: function() {
        return items
      },
      setItems: function(newItems) {
        items = newItems
      },
      addItem: function(item) {
        items.push(item)
      }
    };
    return singleton;
})

and the usage:

require(["showcase/modules/Singleton"], function(Singleton){
  Singleton.addItem('item1')
  console.log(Singleton.getItems())
})
require(["showcase/modules/Singleton"], function(Singleton){
  Singleton.addItem('item2')
  console.log(Singleton.getItems())
})

the output will be:

Instance initialization ["item1"] ["item1", "item2"]

Is it possible to define and use the module in such way, that I could switch in the module definition, if I want to use prototype or singleton scope? So in my case, without changing the usage, I'd get:

Instance initialization ["item1"] Instance initialization ["item2"]

I'm using RequireJS from Dojo, just in case of syntax differences

Upvotes: 1

Views: 462

Answers (1)

Dimitri Mestdagh
Dimitri Mestdagh

Reputation: 44725

Well, first of all the problem is that when you import a module using an AMD loader, you will actually get an instance, but the second time you import the same module, the same instance is actually returned (problem 1).

To overcome this problem you should use the factory design pattern to get your instance and also translate your singleton object to a class that can be instantiated (problem 2). Your factory could have a method called getInstance() that accepts a boolean parameter that can toggle between singleton/prototype.

So without changing your usage you won't be able to do this because of the problems I just addressed. The best solution I can come up with (with a factory) is:

Singleton.js

define([], function() {
    console.log('Instance  initialization');

    // Singleton class
    var Singleton = function() {
      this.items = [];
      this.getItems = function() {
        return this.items;
      };
      this.setItems = function(newItems) {
        this.items = newItems;
      };
      this.addItem = function(item) {
        this.items.push(item);
      }
    };

    // Factory
    var factory = {
      singletonInstance: new Singleton(),
      getInstance: function(/** Boolean */ isSingleton) {
        if (isSingleton === true) {
          return this.singletonInstance;
        } else {
          return new Singleton();
        }
      }
    };
    return factory;
});

Usage (singleton)

require(["app/Singleton"], function(singletonFactory){
  var Singleton = singletonFactory.getInstance(true);
  Singleton.addItem('item1');
  console.log(Singleton.getItems());
});
require(["app/Singleton"], function(singletonFactory){
  var Singleton = singletonFactory.getInstance(true);
  Singleton.addItem('item2');
  console.log(Singleton.getItems());
});

Usage (multiple instances)

require(["app/Singleton"], function(singletonFactory){
  var Singleton = singletonFactory.getInstance(false);
  Singleton.addItem('item3');
  console.log(Singleton.getItems());
});
require(["app/Singleton"], function(singletonFactory){
  var Singleton = singletonFactory.getInstance(false);
  Singleton.addItem('item4');
  console.log(Singleton.getItems());
});

In case you're interested in a full example, it's on Plunker.

Eventually you could wrap the factory as a plugin so that you could actually do something like:

require([ "app/Object!singleton", "app/Object!prototype" ], function() {

});

However I don't know if RequireJS also supports this (and if I'm understanding well it should be a generic story for both AMD loaders).

Upvotes: 2

Related Questions