【问题标题】:How to handle class object with circular references?如何处理具有循环引用的类对象?
【发布时间】:2016-04-28 11:34:49
【问题描述】:

我正在考虑一个简单的问题。我已经给了一个类,例如Model

class Model {
    constructor(parameters = {}) {
        this.id = parameters.id;
    }
}

如您所见,我们可以创建新的Model 对象,例如:let model = new Model()。更复杂的示例如下所示:

//we have some data given from API maybe?
let parameters = {id: 1};
let model = new Model(parameters );

现在我们正处于我开始徘徊的地方如果给定id的对象已经存在怎么办

问题是我应该使用什么模式来实例化具有给定id 的对象一次?

让我们更进一步:如果我们将获得嵌套对象带有循环引用?假设我们有另一个名为 AnotherModel 的类,我们的代码如下所示:

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));
            }
        }
    }
}

所以AnotherModel 包含Models 的集合,Model 对象包含对AnotherModel 的引用。

解决此问题的好方法是什么?我们想要实现的是只有一个具有相同 id 的对象。

我的想法是做某种 ObjectPool,我将存储给定类的所有对象,当实例化新对象时,如果它不存在,我们的池将创建一个新对象或返回现有对象?

但这里有个小缺点,例如,如果我们已经编写了一些代码,我们将不得不重构并将我们实例化它们的方式从 new Model() 更改为 ObjectPool.get(Model, parameters)

你有什么想法?

【问题讨论】:

  • 在上面的场景中,是先创建了AnotherModel 的实例吗?还是可以单独创建ModelAnotherModel 的实例?我不确定我是否理解您的代码的工作方式。
  • 个别而言,如果我们先创建AnotherModel Model 无关紧要
  • 如果这是一个愚蠢的问题,我很抱歉,但是两个组件是否有必要保持对彼此的引用?尤其是这条线对我来说意义不大:: new AnotherModel(parameters.anotherModel);(从模型创建 AnotherModel 而不传递 Model 本身)
  • 因为我们的想法是我们得到一些数据形式的 API,例如,它可能是一个非常不同的情况,例如我们将得到 AnotherModel 和它 Model's 那么它更容易在AnotherModel 构造函数中创建model 对象你不觉得吗?我们还可以单独下载数据,例如 2 AnotherModels,如果他们收集相同的 Model 怎么办?我们不想再次创建相同的模型对象
  • 没有什么可以阻止构造函数使用对象池。无需重写现有代码。

标签: javascript oop ecmascript-6


【解决方案1】:

您可以使用对象池(在类上或类外)来跟踪您的实例。通过在构造函数中定义它,您仍然可以使用以下方法实例化模型:

new Model();
new AnotherModel();

如果池中已经存在id,则只需返回现有实例即可。

课外:

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 = [];

        //...
    }
}

或者作为类(不是实例)上的(不可枚举、不可写、不可配置)属性:

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: {}
});

正如@Vardius 添加的那样,还可以创建一个可以扩展的伪抽象类(因为 JS 没有抽象类)。使用new.target.name,可以在抽象类的池中创建一个命名空间:

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: {} });

【讨论】:

  • 如果我创建一个抽象类AbstractModel 并为这个类定义属性pool 然后所有其他模型都将扩展它。它会工作吗?
  • 我必须定义矩阵然后为多个类存储constructor.name
  • 可能不是,因为pool 不应该是实例 (this.pool) 或原型 (AbstractModel.prototype.pool) 属性。如果它是一个实例属性,它将无法工作,因为它会为每个实例创建。除非您可以在 AbstractModel 构造函数中将池作为 AbstractModel 类的属性写入并为所有子类保留命名空间。
  • 这是我的例子:pastebin.com/PMjUA3rfexport class Entity { constructor(parameters = {}) { if (Entity.pool[this.constructor.name] &amp;&amp; Entity.pool[this.constructor.name][parameters.id] instanceof Entity) { return Entity.pool[this.constructor.name][parameters.id]; } Entity.pool[this.constructor.name][parameters.id] = this; } } Object.defineProperty(Entity, 'pool', { value: {}, writeable: true, configurable: true });
  • 如果您认为没问题,请将其包含在您的答案中以备将来使用
猜你喜欢
  • 2014-04-19
  • 1970-01-01
  • 2017-01-03
  • 2011-04-12
  • 2012-01-17
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
  • 2015-03-02
相关资源
最近更新 更多