Development:Design UndoRedo: Unterschied zwischen den Versionen

Aus StellSi-Hilfewiki
Zur Navigation springen Zur Suche springen
(Die Seite wurde neu angelegt: „Kategorie:Design_Allgemein_Editoren“)
 
Keine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
Status: Entwurf zur Diskussion
= Grundgedanken =
Das UndoRedo-System wird von den Klassen, die speicherbare Member besitzen, transparent intern gelöst. Dabei wird in den entsprechenden Setter-Methoden der Wert nicht mehr direkt gesetzt, sondern stattdessen das nötige UndeRedoObjekt generiert und an den UndoRedoManager übergeben. Dieser führt dann das Kommandoobjekt aus. Die Schnittstelle der Klasse nach außen wird dadurch nicht beeinflusst.
Eine Änderung des Zustandes der Klasse muss dabei immer zu einem konsistenten Datenstand führen. Daher gelten folgende Regeln:
1) Datenduplikation muss vermieden werden
2) Alle Klassen, die von Daten einer anderen Klasse abhängen, müssen bei einer Änderung benachrichtigt werden. Dafür kann z.B. das Beobachtermuster genutzt werden. Auf diese Art müssen z.B. die GUI-Elemente des Editors benachrichtigt werden, aber natürlich auch die eigentliche Simulationslogik u.ä.
= Klassenhierarchie =
TODO in Klassendiagramm umwandeln
  (Beispielhaft: DateiVerwaltung)
enthält genau einen
  UndoRedoManager
enthält Liste
  UndoRedoObjekt
ist Basisklasse von
  UndoRedoGeneric<typedef TypeData, TODO Funktionen die Verhalten spezifizieren>
  UndoRedo{SpeziellerName}
TODO Präfix UndoRedo so in Ordnung oder lieber "Command" o.ä.?
= Klassen =
== UndoRedoManager ==
=== Konstruktor ===
Nimmt entgegen:
1) Wert, der gesetzt werden soll
2) Objekt, bei dem der Wert gesetzt werden soll
3) Do-Methode des Objekts, bei dem der Wert gesetzt werden soll
4) Undo-Methode des Objekts, bei dem der Wert gesetzt werden soll
=== Methode bool hasChangesSinceLastSafe ===
return lastSafe!=actualState
=== Methode redo() ===
++actualState;
actualState->do();
emitHasChanged();
=== Methode undo() ===
actualState->undo();
--actualState;
=== Methode appendCommand(UndoRedoObject* command) ===
list.delete(actualState+1, list.end();
if (!actualState->tryAppendAndRun(command))
  list.append(command);
  command->do();
actualState=list.end()-1;
=== Methode fileHasBeenSaved() ===
lastSafe=actualState;
=== Private Methode emitHasChanged() ===
if (lastHasChanged!=hasChangesSinceLastSave)
  lastHasChanged=hasChangesSinceLastSave;
  emit hasChangesChanged(lastHasChanged);
=== Signal hasChangesChanged(bool fileIsChanged) ===
=== Member list ===
Liste der Undo-Redo-Objekte
=== Member lastSafe ===
Iterator auf den Listeneintrag, der dem letzten Speichern entspricht
=== Member actualState ===
Iterator auf den Listeneintrag, der gerade aktuell ist
== UndoRedoObjekt ==
=== Methode Do ===
Führt den Befehl aus, speichert den alten Wert als Vorbereitung für undo()
=== Methode Undo ===
Führt den Befehl aus,  speichert den alten Wert als Vorbereitung für do() (was in dem Fall ein redo ist)
=== Methode tryAppendAndRun(UndoRedoObjekt* command) ===
wenn Typ übereinstimmt, state stimmt und das Kommandoobjekt darauf ausgelegt ist: Führe aktuelle Änderung mit der letzten Änderung zusammen. Beispiel: Ein String wird mehrere Male hintereinander geändert (z.B. jeder Buchstabe löst ein Änderungssignal aus). In dem Fall sollte nicht für jeden Buchstaben ein eigenes Kommandoobjekt erzeugt werden, das macht die Änderungen so unpraktisch...
=== Member state ===
Hält die Information, ob das Objekt ausgeführt wurde oder rückgängig gemacht wurde
[[Kategorie:Design_Allgemein_Editoren]]
[[Kategorie:Design_Allgemein_Editoren]]

Version vom 8. Dezember 2015, 13:10 Uhr

Status: Entwurf zur Diskussion

Grundgedanken

Das UndoRedo-System wird von den Klassen, die speicherbare Member besitzen, transparent intern gelöst. Dabei wird in den entsprechenden Setter-Methoden der Wert nicht mehr direkt gesetzt, sondern stattdessen das nötige UndeRedoObjekt generiert und an den UndoRedoManager übergeben. Dieser führt dann das Kommandoobjekt aus. Die Schnittstelle der Klasse nach außen wird dadurch nicht beeinflusst.

Eine Änderung des Zustandes der Klasse muss dabei immer zu einem konsistenten Datenstand führen. Daher gelten folgende Regeln: 1) Datenduplikation muss vermieden werden 2) Alle Klassen, die von Daten einer anderen Klasse abhängen, müssen bei einer Änderung benachrichtigt werden. Dafür kann z.B. das Beobachtermuster genutzt werden. Auf diese Art müssen z.B. die GUI-Elemente des Editors benachrichtigt werden, aber natürlich auch die eigentliche Simulationslogik u.ä.

Klassenhierarchie

TODO in Klassendiagramm umwandeln

 (Beispielhaft: DateiVerwaltung)

enthält genau einen

 UndoRedoManager

enthält Liste

 UndoRedoObjekt

ist Basisklasse von

 UndoRedoGeneric<typedef TypeData, TODO Funktionen die Verhalten spezifizieren>
 UndoRedo{SpeziellerName}

TODO Präfix UndoRedo so in Ordnung oder lieber "Command" o.ä.?

Klassen

UndoRedoManager

Konstruktor

Nimmt entgegen: 1) Wert, der gesetzt werden soll 2) Objekt, bei dem der Wert gesetzt werden soll 3) Do-Methode des Objekts, bei dem der Wert gesetzt werden soll 4) Undo-Methode des Objekts, bei dem der Wert gesetzt werden soll

Methode bool hasChangesSinceLastSafe

return lastSafe!=actualState

Methode redo()

++actualState; actualState->do(); emitHasChanged();

Methode undo()

actualState->undo(); --actualState;

Methode appendCommand(UndoRedoObject* command)

list.delete(actualState+1, list.end(); if (!actualState->tryAppendAndRun(command))

 list.append(command);
 command->do();

actualState=list.end()-1;

Methode fileHasBeenSaved()

lastSafe=actualState;

Private Methode emitHasChanged()

if (lastHasChanged!=hasChangesSinceLastSave)

 lastHasChanged=hasChangesSinceLastSave;
 emit hasChangesChanged(lastHasChanged);

Signal hasChangesChanged(bool fileIsChanged)

Member list

Liste der Undo-Redo-Objekte

Member lastSafe

Iterator auf den Listeneintrag, der dem letzten Speichern entspricht

Member actualState

Iterator auf den Listeneintrag, der gerade aktuell ist

UndoRedoObjekt

Methode Do

Führt den Befehl aus, speichert den alten Wert als Vorbereitung für undo()

Methode Undo

Führt den Befehl aus, speichert den alten Wert als Vorbereitung für do() (was in dem Fall ein redo ist)

Methode tryAppendAndRun(UndoRedoObjekt* command)

wenn Typ übereinstimmt, state stimmt und das Kommandoobjekt darauf ausgelegt ist: Führe aktuelle Änderung mit der letzten Änderung zusammen. Beispiel: Ein String wird mehrere Male hintereinander geändert (z.B. jeder Buchstabe löst ein Änderungssignal aus). In dem Fall sollte nicht für jeden Buchstaben ein eigenes Kommandoobjekt erzeugt werden, das macht die Änderungen so unpraktisch...

Member state

Hält die Information, ob das Objekt ausgeführt wurde oder rückgängig gemacht wurde