mercredi 29 mai 2019

Is creating deeply nested objects which get modified a good practice?

In an attempt to learn Javascript, I'm building a simple browser game. I have certain objects in the game in the following hierarchy:

  • Locations
    • Locations...
      • Elements
        • Interactions
      • ...
    • Elements
      • Interactions
        • Items
      • Other information (e.g. stats)

Basically, each location has a parent location, and possibly children locations. Each location has elements, which has interactions, which hold items, as well as other identifier objects.

Each location, element, and interaction is unique, and can only have one parent (e.g. element 10 will only be in the child of only one location). Items and identifier objects can have multiple parents. Then, I build the game, and JSON stringify the data, and only rebuild when prompted to do so in the web app.

During the build, I'm pretty okay with it taking longer, but would like to optimize for speed and memory during gameplay.

What is the best practice to store the hierarchical relationships?

Currently, I'm doing the following: Each location has the unique location id of its parent, children, and elements. Each element has the parent location id and the id of the interactions and other identifiers. Each interaction has its parent element id, and the id of the items, if applicable. Each item and identifier objects do not have parents or children. Then the game build is a map of the following:

Location: Map of each location id to the data

Element: Map of each element id to the data

Interaction: Map of each interaction id to the data

Item: Map of each itemId to the data.

Identifier objects: Map from each identifier id to the data

In some pseudocode this is:

let _LocNames = {};
function _Location(locationId, parentId, context) {
    this.locationId = locationId;
    this.parentId = parentId;
    this.children = []; // array of strings
    this.elements = []; // array of strings
    // ... other stuff
    _LocNames[locationId] = this;
}

_Location.prototype.addChild = function(childId) {
    new _Location(childId, this.locationId, context);
    this.children.push(childId);
};

_Location.prototype.addElement = function(elementId) {
    this.elements.push(elementId);
};

let _ElementNames = {};
function _Element(elementId, parentId, context) {
    this.elementId = elementId;
    this.parentId = parentId;
    this.interactions = []; // array of strings
    // ... other stuff
    _LocNames[parentId].addElement(elementId);
    _ElementNames[elementId] = this;
}

_ElementNames.prototype.addInteraction = function(interactionId) {
    this.interactions.push(interactionId)
}

let _InteractionNames = {};
function _Interaction(interactionId, elementId, context) {
    this.interactionId = interactionId;
    this.elementId = elementId;
    this.items = context.items; // array of itemIds (strings)
    // ... other stuff
    _ElementNames[elementId].addInteraction(interactionId);
    _InteractionNames[interactionId] = this;
}

let _ItemNames = {};
function _Item(itemId, context) {
    this.itemId = itemId;
    // ... other stuff
    _ItemNames[itemId] = this;
}

let Data = {};
Data["Location"] = _LocNames;
Data["Element"] = _ElementNames;
// so on so forth

// The game build would then be
JSON.stringify(Data)

Then, suppose I want to do something with all elements at a location:

Data.Loc[locId].elements.map(elementId => foo(Data.Element[elementId]))

This works fine, but would a better design pattern be instead to make a more deeply nested hierarchy? For example something like:

function _Location(locationId, parent, context) {
    this.locationId = locationId;
    this.parentId = parent;
    this.children = {};
    this.elements = {};
}

let _baseLocation = new _Location("BASE", null, null);

function _findParent(id) {
    // Do something starting at the root node (_baseLocation)
    // and find the parent using some kind naming / design.
    return parent; // Note: the object, not the parentId
}

addChild(childId, context) {
    let parent = _findParent(childId);
    parent.children[childId] = new _Location(childId, parent, context);
}

function _Element(elementId, parent, context) {
    this.elementId = elementId;
    this.parent = parent;
    this.interactions = {};
}

addElement(elementId, context) {
    let parent = _findParent(elementId);
    parent.elements[elementId] = new _Element(elementId, parent, context);
}

// so on for the others lower on the hierarchy. 
// Then the game build would look like
JSON.stringify(_baseLocation)

Then during the gameplay, as it is impossible to jump for more than one level in the hierarchy, if we go back one level, we can just go to the parent.

In the first design, the deepest object is 4 in (Data -> Object Type -> Object Name -> ifArray/Map -> only strings/numbers), while in the second it could be really deep.

It would seem to be that storing the names, and having a more shallow design is more beneficial, and useable. If that's true, would it also be for an extremely large dataset?

Aucun commentaire:

Enregistrer un commentaire