I am thinking about simple problem. I have given an class for example Model
class Model {
constructor(parameters = {}) {
this.id = parameters.id;
}
}
so as you can see we can create new Model
objects like: let model = new Model()
. More complex example would look like this:
//we have some data given from API maybe?
let parameters = {id: 1};
let model = new Model(parameters );
And here we are at the point where i started to wander What if the object with given id
already exists ?
The question is what pattern should i use to instantiate object with given id
only once?
Let's go further: what if we will get nested objects with circural references ? Let assume we got another class called AnotherModel
and our code looks like:
class Model {
constructor(parameters = {}) {
this.id = parameters.id;
this.anotherModel= nulld;
if (parameters.anotherModel) {
this.anotherModel= parameters.anotherModel instanceof AnotherModel
? parameters.anotherModel
: new AnotherModel(parameters.anotherModel);
}
}
}
class AnotherModel {
constructor(parameters = {}) {
this.id = parameters.id;
this.models = [];
if (parameters.models) {
for (let i = 0; i < parameters.models.length; i++) {
let model = parameters.models[i];
model.anotherModel= this;
this.models.push(new Model(model));
}
}
}
}
So AnotherModel
contains a collection of Model
s and Model
object contains reference to AnotherModel
.
What is the nice way to resolve this issue ? What we want to achive is to have only one object with the same id.
What i was thinking is to do some kind of ObjectPool where i will store all objects for given class or classes and when new object is instantiated our pool would create a new one if it does not exist or return the existing one?
But here is a little disadventage, if for example we already have written some code we would have to refactore and change the way we instatiate them from new Model()
to ObjectPool.get(Model, parameters)
?
What are your ideas ?
You could use an object pool (either on the class or outside of it) to keep track of your instances. By defining it in the constructor, you can still instantiate models with:
new Model();
new AnotherModel();
If the id
already exists in the pool, you can just return the existing instance.
Outside of the class:
const modelPool = {};
class Model {
constructor(parameters = {}) {
if (modelPool[parameters.id] instanceof Model) {
return modelPool[parameters.id];
}
modelPool[parameters.id] = this;
this.id = parameters.id;
this.anotherModel= null;
// ...
}
}
const anotherModelPool = {};
class AnotherModel {
constructor(parameters = {}) {
if (anotherModelPool[parameters.id] instanceof AnotherModel) {
return anotherModelPool[parameters.id];
}
anotherModelPool[parameters.id] = this;
this.id = parameters.id;
this.models = [];
//...
}
}
Or as a (non-enumerable, non-writeable, non-configurable) property on the class (not the instance):
class Model {
constructor(parameters = {}) {
if (Model.pool[parameters.id] instanceof Model) {
return Model.pool[parameters.id];
}
Model.pool[parameters.id] = this;
this.id = parameters.id;
this.anotherModel= null;
//...
}
}
Object.defineProperty(Model, 'pool', {
value: {}
});
class AnotherModel {
constructor(parameters = {}) {
if (AnotherModel.pool[parameters.id] instanceof AnotherModel) {
return AnotherModel.pool[parameters.id];
}
AnotherModel.pool[parameters.id]
this.id = parameters.id;
this.models = [];
//...
}
}
Object.defineProperty(AnotherModel, 'pool', {
value: {}
});
As added by @Vardius, one can also create a pseudo-abstract class (as JS does not have abstract classes) which can be extended from. Using new.target.name
, a namespace within the pool of the abstract class can be created:
class Entity {
constructor(parameters = {}) {
if (Entity.pool[this.constructor.name] && Entity.pool[this.constructor.name][parameters.id] instanceof Entity) {
return Entity.pool[new.target.name][parameters.id];
}
Entity.pool[new.target.name][parameters.id] = this;
}
}
Object.defineProperty(Entity, 'pool', {value: {} });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With