Reputation: 22867
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:
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
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:
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;
});
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());
});
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