Angenommen, Sie haben einen Analysebaum, einen abstrakten Syntaxbaum und ein Kontrollflussdiagramm, die jeweils logisch von dem vorherigen abgeleitet sind. Im Prinzip ist es einfach, jedes Diagramm anhand des Analysebaums zu erstellen. Wie können wir jedoch die Komplexität der Aktualisierung der Diagramme verwalten, wenn der Analysebaum geändert wird? Wir wissen genau, wie der Baum geändert wurde, aber wie kann die Änderung auf eine Weise auf die anderen Bäume übertragen werden, die nicht schwierig zu verwalten ist?
Natürlich kann das abhängige Diagramm aktualisiert werden, indem es jedes Mal, wenn sich das erste Diagramm ändert, einfach von Grund auf neu rekonstruiert wird. Dann gibt es jedoch keine Möglichkeit, die Details der Änderungen im abhängigen Diagramm zu kennen.
Ich habe derzeit vier Möglichkeiten, um dieses Problem zu lösen, aber jede hat Schwierigkeiten.
- Knoten des abhängigen Baums beobachten jeweils die relevanten Knoten des ursprünglichen Baums und aktualisieren sich selbst und die Beobachterlisten der ursprünglichen Baumknoten nach Bedarf. Die konzeptionelle Komplexität kann entmutigend werden.
- Jeder Knoten des ursprünglichen Baums verfügt über eine Liste der abhängigen Baumknoten, die speziell von ihm abhängen. Wenn sich der Knoten ändert, setzt er ein Flag auf die abhängigen Knoten, um sie als fehlerhaft zu markieren, einschließlich der Eltern der abhängigen Knoten ganz unten zur Wurzel. Nach jeder Änderung führen wir einen Algorithmus aus, der dem Algorithmus zum Erstellen des abhängigen Graphen von Grund auf ähnlich ist. Er überspringt jedoch jeden sauberen Knoten und rekonstruiert jeden schmutzigen Knoten, wobei verfolgt wird, ob sich der rekonstruierte Knoten tatsächlich vom schmutzigen Knoten unterscheidet. Dies kann auch schwierig werden.
- Wir können die logische Verbindung zwischen dem ursprünglichen Diagramm und dem abhängigen Diagramm als Datenstruktur darstellen, wie eine Liste von Einschränkungen, die möglicherweise in einer deklarativen Sprache entworfen wurden. Wenn sich das ursprüngliche Diagramm ändert, müssen wir nur die Liste scannen, um festzustellen, welche Einschränkungen verletzt werden und wie sich der abhängige Baum ändern muss, um die Verletzung zu korrigieren. Alle Daten werden als Daten codiert.
- Wir können das abhängige Diagramm von Grund auf neu rekonstruieren, als gäbe es kein vorhandenes abhängiges Diagramm, und dann das vorhandene Diagramm und das neue Diagramm vergleichen, um festzustellen, wie es sich geändert hat. Ich bin mir sicher, dass dies der einfachste Weg ist, da ich weiß, dass Algorithmen zum Erkennen von Unterschieden verfügbar sind, aber alle sind recht rechenintensiv und im Prinzip scheint dies unnötig zu sein, sodass ich diese Option absichtlich vermeide.
Was ist der richtige Weg, um mit solchen Problemen umzugehen? Sicherlich muss es ein Designmuster geben, das das Ganze fast einfach macht. Es wäre schön, für jedes Problem dieser allgemeinen Beschreibung eine gute Lösung zu haben. Hat diese Problemklasse einen Namen?
Lassen Sie mich auf die Probleme eingehen, die dieses Problem verursacht. Dieses Problem tritt an verschiedenen Stellen auf, wenn zwei Teile eines Projekts mit Diagrammen arbeiten, wobei jedes Diagramm eine andere Darstellung derselben Änderung darstellt, die sich während der Ausführung der Software ändert. Es ist wie beim Erstellen eines Adapters für eine Schnittstelle, aber anstatt ein einzelnes Objekt oder eine feste Anzahl von Objekten zu verpacken, müssen wir ein ganzes Diagramm beliebiger Größe umbrechen.
Jedes Mal, wenn ich das versuche, bekomme ich ein verwirrendes, nicht zu wartendes Durcheinander. Der Kontrollfluss von Beobachtern kann schwierig zu verfolgen sein, wenn er kompliziert wird, und der Algorithmus zum Konvertieren eines Graphen in einen anderen ist normalerweise schwierig genug, um zu folgen, wenn er klar angelegt und nicht über mehrere Klassen verteilt ist. Das Problem ist, dass es anscheinend keine Möglichkeit gibt, nur einen einfachen, unkomplizierten Graphkonvertierungsalgorithmus zu verwenden, wenn sich der ursprüngliche Graph ändert.
Natürlich können wir einen gewöhnlichen Graphkonvertierungsalgorithmus nicht direkt verwenden, da dieser nicht anders als von vorne anfangen auf Änderungen reagieren kann. Welche Alternativen gibt es also? Möglicherweise könnte der Algorithmus in einem Continuation-Passing-Stil geschrieben werden, bei dem jeder Schritt des Algorithmus als Objekt mit einer Methode für jeden Knotentyp im Originaldiagramm dargestellt wird, wie ein Besucher. Dann kann der Algorithmus zusammengestellt werden, indem verschiedene einfache Besucher zusammengesetzt werden.
Ein weiteres Beispiel: Angenommen, Sie haben eine GUI, die wie in Java Swing mit JPanels und Layout-Managern angelegt ist. Sie können diesen Prozess vereinfachen, indem Sie verschachtelte JPanels anstelle komplexer Layout-Manager verwenden. So erhalten Sie einen Baum mit verschiedenen Containern, der Knoten enthält, die nur für Layoutzwecke vorhanden und ansonsten bedeutungslos sind. Angenommen, derselbe Baum, der zum Generieren Ihrer GUI verwendet wird, wird auch in einem anderen Teil Ihrer Anwendung verwendet. Anstatt den Baum grafisch darzustellen, arbeitet er mit einer Bibliothek, die einen abstrakten Repräsentationsbaum als Ordnersystem generiert. Um diese Bibliothek verwenden zu können, benötigen wir eine Version des Baums, die nicht über die Layoutknoten verfügt. Die Layoutknoten müssen in ihre übergeordneten Knoten abgeflacht werden.
Eine andere Sichtweise: Das Konzept, mit veränderlichen Bäumen zu arbeiten, verstößt gegen das Gesetz von Demeter . Es wäre nicht wirklich ein Verstoß gegen das Gesetz, wenn der Baum ein Wert wäre, wie es Analysebäume und Syntaxbäume normalerweise sind, aber in diesem Fall wäre dies kein Problem, da nichts auf dem neuesten Stand gehalten werden müsste. Dieses Problem besteht also als direkte Folge eines Verstoßes gegen das Demeter-Gesetz. Wie können Sie dies jedoch generell vermeiden, wenn es in Ihrer Domain anscheinend darum geht, Bäume oder Grafiken zu manipulieren?
Das zusammengesetzte Muster ist ein wunderbares Werkzeug, um ein Diagramm in ein einzelnes Objekt zu verwandeln und das Gesetz von Demeter zu befolgen. Ist es möglich, das zusammengesetzte Muster zu verwenden, um eine Baumart effektiv in eine andere zu verwandeln? Können Sie einen zusammengesetzten Analysebaum so erstellen, dass er sich wie ein abstrakter Syntaxbaum und sogar wie ein Kontrollflussdiagramm verhält? Gibt es eine Möglichkeit, dies zu tun, ohne das Prinzip der Einzelverantwortung zu verletzen ? Das zusammengesetzte Muster führt dazu, dass Klassen jede Verantwortung übernehmen, die sie berühren, aber vielleicht könnte es irgendwie mit dem Strategiemuster kombiniert werden .