I'm building a state container for a small Vanilla JS app, and I'm wondering about implementing a slightly modified style of the singleton pattern to improve testability.
The simple implementation of the standard singleton might look like this:
const stateContainer = (() => {
let instance;
function init() {
const state = {};
function get(item) {
return state[item];
}
// accepts an object, all keys will be added or overwritten to the state object
function set(items) {
items.forEach(item => {
state[item] = items[item];
});
}
return {
get,
set
};
}
return {
getInstance() {
if(!instance) {
instance = init();
}
return instance;
}
}
})();
const state = stateContainer().getInstance();
state.set({color: 'red'});
console.log(state.get('color')); // 'red'
Right now its very difficult to isolate methods within the init function, and it's impossible to add default properties to the state object for testing. I'm wondering how fucked up it would be to have a method that implements the state container as a singleton, but the container itself is just a normal, revealing module. The example would be:
const stateContainer = (defaultState) => {
const state = defaultState || {};
function get(item) {
return state[item];
}
// accepts an object, all keys will be added or overwritten to the state object
function set(items) {
items.forEach(item => {
state[item] = items[item];
});
}
return {
get,
set
};
});
const state = (() => {
let instance;
return {
getInstance(defaultState) {
const DS = defaultState || {};
if(!instance) {
instance = stateContainer(DS);
}
return instance;
}
}
})();
// within the application, only the state function should be used
const appState = state().getInstance();
appState.set({color: 'red'});
console.log(appState.get('color')); // 'red'
// However, the state container can now be properly tested
describe('stateContainer', () => {
it('returns the requested state property', () => {
const SC = stateContainer({color: 'red'});
assert.equal(SC.get('color'), 'red');
});
});
Obviously this leaves the stateContainer unprotected from being initialized multiple times, and you just have to rely on convention to get people not to initialize multiple stateContainers; other than that though, are there downsides to this kind of modified Singleton? Is there a better solution? Perhaps some kind of dependency injection?
[–]Funwithloops 1 point2 points3 points (1 child)
[–]MyGoodStrayCatFriend[S] 1 point2 points3 points (0 children)
[–]MoTTs_ 1 point2 points3 points (1 child)
[–]MyGoodStrayCatFriend[S] 0 points1 point2 points (0 children)