Frage

Ich arbeite an einem kleinen UML-Editor Projekt, in Java, dass ich vor ein paar Monaten begonnen. Nach ein paar Wochen erhielt ich eine Arbeitskopie für ein UML-Klassendiagramm-Editor.

Aber jetzt, ich bin neu zu gestalten es ganz andere Arten von Diagrammen zu unterstützen, eine solche Sequenz, Zustands-, Klassen-, usw. durch die Implementierung eines Graphen Konstruktion Rahmen durchgeführt wird (ich bin von Cay Horstmann stark inspiriert auf die Arbeit Thema mit dem Editor Violet UML).

Redesign ging glatt, bis einer meiner Freunde mir gesagt, dass ich ein Do / Undo functionnality zum Projekt hinzuzufügen vergessen, die, meiner Meinung nach, lebenswichtig ist.

objektorientierte Design-Kurse Erinnerung, dachte ich sofort an Memento und Befehlsmuster.

Hier ist der Deal. Ich habe eine abstrakte Klasse, AbstractDiagram enthält, dass zwei Arraylisten: eine für Knoten zu speichern (so genannte Elemente in meinem Projekt) und die andere für die Kanten (so genannte Verbindungen in meinen Projekten) zu speichern. Das Diagramm wird wahrscheinlich einen Stapel von Befehlen halten, die / Redoed Undoed werden kann. Ziemlich Standard.

Wie kann ich diese Befehle in einer effizienten Art und Weise durchführen? Nehmen wir zum Beispiel, dass ich will einen Knoten bewegen (der Knoten wird ein Interface-Typ namens INode sein und konkrete Knoten von ihm (ClassNode, InterfaceNode, NoteNode, etc.) abgeleitet sein).

Die Positionsinformation wird als Attribut in dem Knoten gehalten wird, so von diesem Attribute in dem Knoten selbst modying wird der Zustand verändert. Wann wird die Anzeige aktualisiert wird sich der Knoten verschoben. Dies ist das Memento Teil des Musters (glaube ich), mit dem Unterschied, dass das Objekt der Staat selbst.

Außerdem, wenn ich einen Klon des ursprünglichen Knotens halten (bevor es bewegt wird), kann ich wieder in der alten Version bekommen. Die gleiche Technik gilt für die in den Knoten (die Klasse oder Interface-Namen, der Text für einen Notizknoten, die Attribute Name, usw.) enthalten sind.

Die Sache ist, wie kann ich ersetzen, in dem Diagramm, um den Knoten mit seinem Klon auf Undo / Redo-Operation? Wenn ich das ursprüngliche Objekt klonen, die durch das Diagramm Bezug genommen wird (in der Knotenliste ist), wird der Klon nicht in dem Diagramm verweisen, und das einzige, was den Punkten ist der Befehl selbst! Shoud I umfasst Mechanismen in dem Diagramm einen Knoten für die Suche nach einer ID entsprechend (zum Beispiel), so kann ich, in dem Diagramm, um den Knoten durch seinen Klon (und umgekehrt) ersetzen? Ist es zu der Memento und Befehlsmustern auf, das zu tun? Was ist mit Links? Sie sollten auch beweglich sein, aber ich will keinen Befehl nur für Links (und man nur für Knoten) schaffen, und ich soll die rechte Liste (Knoten oder Links) in der Lage zu ändern je nach Art des Objekts des Befehl bezieht sich auf.

Wie würden Sie vorgehen? Kurz gesagt, habe ich Probleme, die den Zustand eines Objekts in einem Befehl / Erinnerungsmuster, so dass sie effizient und das ursprüngliche Objekt wieder in dem Diagramm Liste wiederhergestellt werden können, und abhängig von dem Objekttyp (Knoten bzw. Link).

Vielen Dank!

Guillaume.

P. S .: Wenn ich nicht klar bin, sagen Sie mir, und ich werde meine Botschaft klären (wie immer!).

Bearbeiten

Hier ist meine eigentliche Lösung, dass ich die Umsetzung begonnen, diese Frage vor der Veröffentlichung.

Zuerst habe ich eine AbstractCommand Klasse wie folgt definiert:

public abstract class AbstractCommand {
    public boolean blnComplete;

    public void setComplete(boolean complete) {
        this.blnComplete = complete;
    }

    public boolean isComplete() {
        return this.blnComplete;
    }

    public abstract void execute();
    public abstract void unexecute();
}

Dann wird jede Art von Befehl implementiert eine konkrete Ableitung von AbstractCommand verwendet wird.

Also ich habe einen Befehl, ein Objekt zu bewegen:

public class MoveCommand extends AbstractCommand {
    Moveable movingObject;
    Point2D startPos;
    Point2D endPos;

    public MoveCommand(Point2D start) {
        this.startPos = start;
    }

    public void execute() {
        if(this.movingObject != null && this.endPos != null)
            this.movingObject.moveTo(this.endPos);
    }

    public void unexecute() {
        if(this.movingObject != null && this.startPos != null)
            this.movingObject.moveTo(this.startPos);
    }

    public void setStart(Point2D start) {
        this.startPos = start;
    }

    public void setEnd(Point2D end) {
        this.endPos = end;
    }
}

Ich habe auch eine MoveRemoveCommand (zu ... Umzug oder ein Objekt / Knoten entfernen). Wenn ich die ID der instanceof-Methode verwenden, ich habe nicht das Diagramm auf den aktuellen Knoten oder Verbindung geben, so dass sie sich aus dem Diagramm entfernen (was eine schlechte Idee ist, glaube ich).

AbstractDiagram Diagramm;     Hinzufügbarer obj;     AddRemoveType Typ;

@SuppressWarnings("unused")
private AddRemoveCommand() {}

public AddRemoveCommand(AbstractDiagram diagram, Addable obj, AddRemoveType type) {
    this.diagram = diagram;
    this.obj = obj;
    this.type = type;
}

public void execute() {
    if(obj != null && diagram != null) {
        switch(type) {
            case ADD:
                this.obj.addToDiagram(diagram);
                break;
            case REMOVE:
                this.obj.removeFromDiagram(diagram);
                break;
        }
    }
}

public void unexecute() {
    if(obj != null && diagram != null) {
        switch(type) {
            case ADD:
                this.obj.removeFromDiagram(diagram);
                break;
            case REMOVE:
                this.obj.addToDiagram(diagram);
                break;
        }
    }
}

Schließlich habe ich eine ModificationCommand, die verwendet wird, um die Informationen eines Knotens oder Link (Klassenname, etc.) zu ändern. Dies kann in Zukunft mit dem Verschieben zusammengefügt werdenBefehl. Diese Klasse ist jetzt leer. Ich werde wahrscheinlich mit einem Mechanismus, um die ID Sache tun, um zu bestimmen, ob das geänderte Objekt ein Knoten oder eine Kante (über instanceof oder eine spezielle denotion in der ID).

Ist das eine gute Lösung?

War es hilfreich?

Lösung

Ich glaube, Sie müssen nur Ihr Problem in kleinere zerfallen.

Erstes Problem: F: Wie die Schritte in der App mit dem Memento / Befehlsmuster repräsentieren? Zunächst einmal, ich habe keine Ahnung, wie genau Ihre Werke App aber hoffentlich werden Sie sehen, wo ich mit diesem gehe. Sagen, dass ich einen ClassNode auf dem Diagramm eingefügt werden soll, die mit den folgenden Eigenschaften

{ width:100, height:50, position:(10,25), content:"Am I certain?", edge-connections:null}

Das wäre als ein Befehlsobjekt gewickelt. Sagen Sie, dass zu einem DiagramController geht. Dann in der Verantwortung des Diagramm-Controller kann sein, um diesen Befehl aufzuzeichnen (push auf einen Stapel würde meine Wette) und geben Sie den Befehl an einen DiagramBuilder zum Beispiel. Die DiagramBuilder würden tatsächlich verantwortlich für die Anzeige zu aktualisieren.

DiagramController
{
  public DiagramController(diagramBuilder:DiagramBuilder)
  {
    this._diagramBuilder = diagramBuilder;
    this._commandStack = new Stack();
  }

  public void Add(node:ConditionalNode)
  {
    this._commandStack.push(node);
    this._diagramBuilder.Draw(node);
  }

  public void Undo()
  {
    var node = this._commandStack.pop();
    this._diagramBuilderUndraw(node);
  }
}

Manche Dinge wie das sollte es tun, und natürlich wird es viele Details zu klären. By the way, haben die mehr Eigenschaften Ihre Knoten desto detaillierter Undraw sein haben wird.

eine ID Mit dem Befehl im Stapel auf das Element zu verknüpfen gezogen könnte eine gute Idee sein. Das könnte wie folgt aussehen:

DiagramController
{
  public DiagramController(diagramBuilder:DiagramBuilder)
  {
    this._diagramBuilder = diagramBuilder;
    this._commandStack = new Stack();
  }

  public void Add(node:ConditionalNode)
  {
    string graphicalRefId = this._diagramBuilder.Draw(node);
    var nodePair = new KeyValuePair<string, ConditionalNode> (graphicalRefId, node);
    this._commandStack.push(nodePair);
  }

  public void Undo()
  {
    var nodePair = this._commandStack.pop();
    this._diagramBuilderUndraw(nodePair.Key);
  }
} 

An diesem Punkt Sie absolut nicht haben müssen das Objekt, da Sie die ID haben, aber es wird hilfreich sein, sollten Sie sich entscheiden, auch Redo-Funktionalität zu implementieren. Ein guter Weg, um die ID zu erzeugen, um die Knoten einen Hash-Code-Methode für sie außer der Tatsache zu realisieren wäre, dass Sie nicht nicht die Knoten so duplizieren garantiert werden würde, der den Hash-Code verursachen würde identisch sein.

Der nächste Teil des Problems ist in Ihren DiagramBuilder, weil Sie, um herauszufinden, sind versucht, wie zum Teufel mit diesen Befehlen zu beschäftigen. Denn alles, was ich sagen kann ist, um wirklich nur sicherstellen, dass Sie eine inverse Aktion für jede Art von Komponente erstellen können Sie hinzufügen können. Um die Entkoppelung zu behandeln Sie am Rand-Verbindungseigenschaft aussehen können (Links in Ihrem Code glaube ich) und teilt jeden der Randverbindungen, die sie von dem spezifischen Knoten zu trennen. Ich würde davon ausgehen, dass beim Abschalten sie sich in geeigneter Weise neu zu zeichnen könnten.

Um ein bisschen zusammenfassen, würde ich empfehlen, keinen Verweis auf den Knoten in dem Stapel zu halten, sondern nur eine Art von Token, das an diesem Punkt einen bestimmten Knoten des Staat darstellt. Auf diese Weise können Sie den gleichen Knoten in Ihrem Rückgängig-Stapel an mehreren Stellen darzustellen, ohne sie auf das gleiche Objekt beziehen.

Post, wenn Sie Q haben. Dies ist ein komplexes Thema.

Andere Tipps

In meiner bescheidenen Meinung nach, bist du es in einer komplizierteren Art und Weise zu denken, als es wirklich ist. Um zum vorherigen Zustand, Klon des gesamten Knotens zurückkehren wird nicht benötigt. Vielmehr jedes * * Command Klasse haben -

  1. Verweis auf den Knoten wird Einwirken auf,
  2. memento Objekt (mit Zustandsvariablen gerade genug für den Knoten zurückzukehren)
  3. execute () Methode
  4. rückgängig machen () -Methode.

Da Befehlsklassen Bezug auf die Knoten haben, brauchen wir ID-Mechanismus nicht auf Objekte in dem Diagramm zu entnehmen.

Im Beispiel aus Ihrer Frage, wollen wir einen Knoten in eine neue Position bewegen. Dafür haben wir eine NodePositionChangeCommand Klasse.

public class NodePositionChangeCommand {
    // This command will act upon this node
    private Node node;

    // Old state is stored here
    private NodePositionMemento previousNodePosition;

    NodePositionChangeCommand(Node node) {
        this.node = node;
    }

    public void execute(NodePositionMemento newPosition) {
        // Save current state in memento object previousNodePosition

        // Act upon this.node
    }

    public void undo() {
        // Update this.node object with values from this.previousNodePosition
    }
}
  
    

Was ist mit Links? Sie sollten auch beweglich sein, aber ich will keinen Befehl nur für Links (und man nur für Knoten) erstellen.

  

las ich in Buch GoF (in Diskussion Memento-Muster), die sich bewegen von Verbindung mit einer Änderung der Position von Knoten, die durch irgendeine Art von Constraint-Solver behandelt werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top