Wie speichere ich mehrere Ansichten von Daten im Speicher?

8

Ich habe ein paar Module. Ich kann diese Module in verschiedene Kategorien unterteilen, die vollständig sind und sich nicht überschneiden. Zum Beispiel drei Kategorien mit IDs, die als ,, und ausgedrückt Animalwerden Vegetablekönnen Mineral. Ich zerlege diese Kategorien weiter in Unterkategorien, die wiederum unterschiedlich, vollständig und nicht überlappend sind. Zum Beispiel ids , die ausgedrückt werden kann als Mammal, Reptile, Legume, Root, Rock, Gem. Schließlich unterhalb dieser Kategorien gibt es die Module selbst, zum Beispiel Cat, Dog, Iguana, Bean, Quartz, Emerald, usw.

Kategoriehierarchie

Hier sind meine häufigsten Anwendungsfälle:

  1. Ich muss verschiedene Methoden für alle Module aufrufen.
  2. Ich muss einen flachen Schnappschuss des aktuellen Status aller Daten über alle Module hinweg erhalten.
  3. Ich muss verschiedene Methoden für alle Module in einer bestimmten Kategorie (aber nicht für eine Unterkategorie) aufrufen.
  4. Ich muss verschiedene Methoden für ein bestimmtes Modul basierend auf seiner bekannten ID aufrufen.
    • Dies kann ein "etwas tun" oder ein "mir einige Daten über sich selbst erzählen" sein.
  5. Ich muss aggregierte Daten zu allen Modulen in einer bestimmten Kategorie (aber nicht in einer Unterkategorie) speichern.

Wie soll ich diese Daten speichern?

Einige andere relevante Fakten:

  • Die Kategorien werden zur Laufzeit festgelegt
    • Daher teilen sich die Module der untersten Ebene eine gemeinsame Schnittstelle.
  • Sobald sie eingerichtet sind, ändern sie sich in diesem bestimmten Lauf nicht mehr - sie basieren auf Daten in Konfigurationsdateien.

Folgendes mache ich derzeit:

  1. Ich habe eine Klasse, die a enthält Map<Category, CategoryDataStructure>. Diese Klasse verwaltet auch eine separate Collection<Module> Ansicht der Daten zur Verwendung mit Anforderung Nr. 2.
  2. CategoryDataStructurehat Delegierungsmethoden verkettet, die den Methodenaufruf über die Kette senden SubCategoryDataStructure.
  3. CategoryDataStructure speichert auch die in Anforderung 5 verwendeten aggregierten Daten.

Es funktioniert, aber es ist ehrlich gesagt ziemlich unhandlich. Das Ganze ist zustandsbehaftet / veränderlich und schwer zu ändern. Wenn ich neues Verhalten hinzufügen möchte, muss ich es an vielen Stellen hinzufügen. Derzeit haben die Datenstrukturen selbst auch viel Geschäftslogik; die Delegierungsmethoden. Außerdem muss die übergeordnete Datenstruktur viel Geschäftslogik ausführen, um ein bestimmtes Modul zu erstellen, und bei Bedarf die übergeordnete Datenstruktur und bei Bedarf die übergeordnete Datenstruktur.

Ich möchte die Datenverwaltungslogik irgendwie von der Datenstruktur selbst trennen, aber aufgrund der Verschachtelung ist es kompliziert. Hier sind einige andere Optionen, die ich in Betracht gezogen habe:

  1. Erstellen Sie einen einfachen Map<Category, Map<Subcategory, Module>>Code und legen Sie den gesamten Code in einer anderen Klasse ab, um seinen Status beizubehalten. Mein Anliegen dabei sind die Anforderungen Nr. 1 und Nr. 2. Es wird schwierig sein, die Ansicht konsistent zu halten, da ich jetzt zwei verschiedene Datenstrukturen habe, die dieselben Daten darstellen.
  2. Machen Sie alles in einer flachen Datenstruktur und durchlaufen Sie die gesamte Struktur, wenn Sie nach einer bestimmten Kategorie oder Unterkategorie suchen.
durron597
quelle
Haben Sie darüber nachgedacht, die Verantwortung für die Ermittlung der zu inspizierenden oder zu verwendenden Objekte in der Hierarchie durch die Verwendung eines oder mehrerer Besucher aufzulösen?
@Snowman Ich habe diese Option nicht in Betracht gezogen. Schlagen Sie vor, die Daten flach zu speichern und dann, wenn ich eine Methode aufrufen muss, den Besucher an alle zu senden und zu prüfen, ob in der handleVisitorKlasse etwas passieren muss ?
Durron597
Es ist etwas komplizierter, ich wollte nur sicherstellen, bevor ich eine ganze Antwort schreibe.
Vielleicht könnten Sie die Top-Down-Struktur umdrehen und von unten nach oben vorgehen: thing.getType () gibt "Animal" zurück. Das ist ziemlich flach.
noumenal

Antworten:

6

Es scheint, dass das Hauptproblem hier darin besteht, dass Sie Objekte in einer Hierarchie basierend auf ihrer Identität angeordnet haben, diese jedoch nicht hierarchisch verwenden.

Eine Analogie wäre, Dateien in Verzeichnissen basierend auf ihrem Dateityp zu speichern, aber jedes Verzeichnis zu durchsuchen und nur bestimmte basierend auf anderen Kriterien als dem Typ zu laden.


Ich möchte die Datenverwaltungslogik irgendwie von der Datenstruktur selbst trennen, aber aufgrund der Verschachtelung ist es kompliziert. Hier sind einige andere Optionen, die ich in Betracht gezogen habe:

Dies ist ein gutes Ziel, und es gibt eine einfache Möglichkeit, die Verantwortlichkeiten ohne einen größeren Refactor aufzuteilen: Verwenden Sie Besucher .

Die Idee ist, dass Sie diese Logik in den Besucher selbst einfügen, wenn Sie nur bestimmte Elemente in der Hierarchie überprüfen oder bearbeiten müssen. Sie können dann mehrere Besucher schreiben, die jeweils unterschiedliche Elemente bearbeiten und unterschiedliche Aktionen ausführen.

Jede Logikeinheit ist jetzt für einen bestimmten Besucher in sich geschlossen . Dies verbessert die SRP-Fähigkeit Ihres Codes. Wenn Sie ändern müssen, wie eine Operation ausgeführt wird, tun Sie dies nur in dem Besucher, der diese Logik implementiert. Ihre Objekthierarchie sollte unverändert bleiben, abzüglich oberflächlicher Änderungen, um die erforderlichen Daten verfügbar zu machen.

Abhängig von den spezifischen Zielen gibt es mehrere Möglichkeiten, Besucher zu implementieren. Die allgemeine Idee ist jedoch, dass jeder Knoten in der Hierarchie ein Besucherobjekt akzeptiert. Die Implementierung sieht folgendermaßen aus und kann in eine abstrakte übergeordnete Klasse eingefügt werden:

public class Node {
  public void accept(Visitor v) {
    v.accept(this);
    for (Node child : children) {
      child.accept(v);
    }
  }
}

Dies stellt sicher, dass unabhängig davon, wie Sie Ihre Knoten zur Laufzeit verkabeln, jeder Knoten verarbeitet wird.

Ihr Besucher sieht folgendermaßen aus:

public interface Visitor {
  accept(Node n);
}

Bei der Besuchermethode accept(Node)wird die eigentliche Arbeit erledigt. Sie müssen den Knoten untersuchen und unter bestimmten Bedingungen verschiedene Aufgaben ausführen (einschließlich Ignorieren des Knotens).


Beispielsweise können Sie möglicherweise Folgendes tun:

Node root = ...;

// Print information on the hierarchy.
root.accept(new DebugVisitor());

// Does stuff with modules, ignores subcategories.
root.accept(new FrobnicateModulesVisitor());

// Eats vegetables, ignores animals and minerals.
root.accept(new EatYourVegetableVisitor());

Jeder Besucher ist eine in sich geschlossene Klasse, die die Logik für jede Operation enthält, ohne dass Bedenken mit anderen Besuchern oder den Knoten gemischt werden müssen.


quelle
kurze Frage zu Ihrer Note-Klasse: Wo ist "Kinder" definiert ... oder war dies nur ein anschauliches Beispiel?
@Bey nichts ist definiert, das war nur illustrativ. Ich gehe davon aus, dass die Leser mit der Graphentheorie und den Bäumen vertraut sind.
1

Eine tiefe Verschachtelungsebene schlägt vor, dass Sie Aktionen in kleinere Funktionen umgestalten sollten, die mithilfe von return-Anweisungen verkettet werden können. Wenn Sie dieselbe Methode für mehrere Eingaben anwenden müssen, können Sie diese function.apply()in Java 8 nutzen.

Angenommen, die verschiedenen Elemente haben keine gemeinsamen Eigenschaften, können Sie eine Schnittstelle implementieren , für die bestimmte Methoden implementiert werden müssen. Für jede Ebene würde ich eine Schnittstelle schaffen, die dann für jeden Unterknoten ausgefahren ist, beispielsweise: Entity, Phylum, Species. Zusätzlich benötigen Sie drei Klassen für jede der drei Entitäten.

Die Daten können als Eigenschaften der Objektinstanzen gespeichert werden. Um einen flachen Schnappschuss zu erhalten, würde ich die Daten mit iterieren function.apply().

noumenal
quelle
Hallo, danke für deine Antwort. Die Elemente der untersten Ebene haben eine gemeinsame Oberfläche. Ich dachte, das wäre klar, wenn ich sagen würde, dass sie alle zur Laufzeit generiert werden, aber ich habe sie bearbeitet, um einen Satz hinzuzufügen, um diesen Punkt zu verdeutlichen.
Durron597
Es scheint mir, dass Sie eine Schnittstelle auch für das nächste Level brauchen, aber ich könnte mich irren.
noumenal