设计模式:不存储直接引用(内存泄漏和代码维护)

对于一个大型项目,它具有许多相互连接的系统和活动部件(一个视频游戏,需要在不同时间在不同区域保留和使用很多状态),所以我遇到了一个不断增长的代码维护问题,我将任何引用分配给一个创建的类实例对象需要在销毁该实例时进行清理,而不再需要它。寻找所有这些参考可能在哪里变得令人讨厌。

为此,我正在考虑不直接在代码的其他区域中直接存储对实际对象的引用,而只是将它们存储在一个可以通过某些ID访问它们的列表中,并将该ID存储在感兴趣的各方上,因此对于删除,我只需要担心从该对象的包含列表中删除该对象,而不必担心对它的引用隐藏在某个地方,以防止其被垃圾回收并持久保存,而不是我想要的。

这样,对象不再负责删除自己对其他对象的引用。他们应该只能获得对其他对象的临时引用,而不应保留。

所以像:

const Spaceships = {};
class Spaceship {
    constructor(id, captain) {
        // Only actual reference to the object stored.
        Spaceships[id] = this;
        this.id = id;
        if (captain) {
            this.captain = captain;
        }
    }
    destroy() {
        // Can safely remove reference here and be confident it will be GCed.
        delete Spaceships[this.id];
    }
    // ES5 accessor syntax.
    get captain() {
        // If the object no longer exists it won't be returned.
        return Captains[this._captainID];
    }
    set captain(captain) {
        if (Captains[captain.id]) {
            this._captainID = captain.id;
            // Prevent setter call loop.
            if (captain.spaceship !== this) {
                captain.spaceship = this;
            }
        }
    }
    warp(location) {
        console.log(`${this.id}: Warping to ${location}`);
    }
}

const Captains = {};
class Captain {
    constructor(id, spaceship) {
        Captains[id] = this;
        this.id = id;
        if (spaceship) {
            this.spaceship = spaceship;
        }
    }
    destroy() {
        delete Captains[this.id];
    }
    get spaceship() {
        return Spaceships[this._spaceshipID];
    }
    set spaceship(spaceship) {
        if (Spaceships[spaceship.id]) {
            this._spaceshipID = spaceship.id;
            if (spaceship.captain !== this) {
                spaceship.captain = this;
            }
        }
    }
    speak(text) {
        console.log(`${this.id}: ${text}`);
    }
}

const kirk = new Captain("J. T. Kirk");
const enterprise = new Spaceship("NCC-1701", kirk);
enterprise.warp("Betazed");
// Destroy the object, which will make any IDs used to access it no longer work.
enterprise.destroy();
// The object would now still be usable for the scope of the variable it is
// assigned to, and no longer.
enterprise.warp("Vulcan");
// This new object would only receive an ID to another object that is no longer
// accessible. 
const spock = new Captain("Spock", enterprise);
// Calling this now would error as the object cannot be found. Would need to
// wrap each call in a check for object existence to handle errors.
// `if (kirk.spaceship) ...`
kirk.spaceship.warp("Andor");

这个模式有名称吗?

评论