Entwurfsmuster für Engine rückgängig machen


117

Ich schreibe ein Strukturmodellierungswerkzeug für eine Bauingenieuranwendung. Ich habe eine riesige Modellklasse, die das gesamte Gebäude darstellt und Sammlungen von Knoten, Linienelementen, Lasten usw. enthält, die ebenfalls benutzerdefinierte Klassen sind.

Ich habe bereits eine Undo-Engine codiert, die nach jeder Änderung am Modell eine Deep-Copy-Datei speichert. Jetzt begann ich zu überlegen, ob ich anders hätte codieren können. Anstatt die Deep-Copies zu speichern, könnte ich vielleicht eine Liste jeder Modifikatoraktion mit einem entsprechenden umgekehrten Modifikator speichern. Damit ich die umgekehrten Modifikatoren auf das aktuelle Modell anwenden kann, um sie rückgängig zu machen, oder die Modifikatoren, um sie zu wiederholen.

Ich kann mir vorstellen, wie Sie einfache Befehle ausführen würden, die Objekteigenschaften usw. ändern. Aber wie wäre es mit komplexen Befehlen? Zum Beispiel das Einfügen neuer Knotenobjekte in das Modell und das Hinzufügen einiger Linienobjekte, die Verweise auf die neuen Knoten beibehalten.

Wie würde man das umsetzen?


Wenn ich den Kommentar "Algorthim rückgängig machen" hinzufüge, kann ich dann nach "Algorithmus rückgängig machen" suchen und diesen finden? Das habe ich gesucht und etwas gefunden, das als Duplikat geschlossen ist.
Peter Turner

hay, ich möchte auch in der Anwendung, die wir entwickeln, Undo / Redo entwickeln. Wir verwenden das QT4-Framework und müssen viele komplexe Undo / Redo-Aktionen ausführen. Ich habe mich gefragt, ob es Ihnen gelungen ist, Command-Pattern zu verwenden.
Ashika Umanga Umagiliya

2
@umanga: Es hat funktioniert, aber es war nicht einfach. Das Schwierigste war, die Referenzen im Auge zu behalten. Wenn beispielsweise ein Frame-Objekt gelöscht wird, müssen seine untergeordneten Objekte: Knoten, darauf einwirkende Lasten und viele andere Benutzerzuweisungen beibehalten werden, damit sie beim Rückgängigmachen wieder eingefügt werden können. Einige dieser untergeordneten Objekte wurden jedoch mit anderen Objekten geteilt, und die Logik zum Rückgängigmachen / Wiederherstellen wurde recht komplex. Wenn das Modell nicht so groß wäre, würde ich den Erinnerungsansatz beibehalten. es ist viel einfacher zu implementieren.
Ozgur Ozcitak

Es ist ein lustiges Problem, daran zu arbeiten. Überlegen Sie, wie Quellcode-Repos dies tun, wie z. B. svn (sie behalten die Unterschiede zwischen den Commits bei).
Alex

Antworten:


88

Die meisten Beispiele, die ich gesehen habe, verwenden hierfür eine Variante des Befehlsmusters . Jede rückgängig zu machende Benutzeraktion erhält eine eigene Befehlsinstanz mit allen Informationen, um die Aktion auszuführen und zurückzusetzen. Sie können dann eine Liste aller ausgeführten Befehle verwalten und diese nacheinander zurücksetzen.


4
So funktioniert im Grunde die Undo-Engine in Cocoa, NSUndoManager.
ungefähr

33

Ich denke, sowohl Andenken als auch Befehl sind nicht praktikabel, wenn Sie sich mit einem Modell der Größe und des Umfangs befassen, die das OP impliziert. Sie würden funktionieren, aber es wäre viel Arbeit, sie zu warten und zu erweitern.

Für diese Art von Problem müssen Sie meines Erachtens Unterstützung für Ihr Datenmodell einbauen, um differenzielle Prüfpunkte für jedes am Modell beteiligte Objekt zu unterstützen . Ich habe das einmal gemacht und es hat sehr gut funktioniert. Das Wichtigste, was Sie tun müssen, ist, die direkte Verwendung von Zeigern oder Referenzen im Modell zu vermeiden.

Jeder Verweis auf ein anderes Objekt verwendet einen Bezeichner (wie eine Ganzzahl). Wann immer das Objekt benötigt wird, suchen Sie die aktuelle Definition des Objekts aus einer Tabelle. Die Tabelle enthält eine verknüpfte Liste für jedes Objekt, die alle vorherigen Versionen enthält, sowie Informationen darüber, für welchen Prüfpunkt sie aktiv waren.

Das Implementieren von Rückgängig / Wiederherstellen ist einfach: Führen Sie Ihre Aktion aus und richten Sie einen neuen Prüfpunkt ein. Rollback aller Objektversionen auf den vorherigen Prüfpunkt.

Es erfordert etwas Disziplin im Code, hat aber viele Vorteile: Sie benötigen keine tiefen Kopien, da Sie den Modellstatus differenziell speichern. Sie können die Menge an Speicher, die Sie verwenden möchten ( sehr wichtig für Dinge wie CAD-Modelle), entweder nach Anzahl der Wiederholungen oder nach verwendetem Speicher festlegen. Sehr skalierbar und wartungsarm für die Funktionen, die auf dem Modell ausgeführt werden, da sie nichts tun müssen, um das Rückgängigmachen / Wiederherstellen zu implementieren.


1
Wenn Sie eine Datenbank (z. B. SQLite) als Dateiformat verwenden, kann dies fast automatisch erfolgen
Martin Beckett

4
Wenn Sie dies erweitern, indem Sie Abhängigkeiten verfolgen, die durch Änderungen am Modell verursacht wurden, haben Sie möglicherweise ein Rückgängig-Baumsystem (dh wenn ich die Breite eines Trägers ändere und dann an einer separaten Komponente arbeite, kann ich zurückkehren und rückgängig machen der Träger wechselt, ohne das andere Zeug zu verlieren). Die Benutzeroberfläche dafür ist zwar etwas unhandlich, aber viel leistungsfähiger als ein herkömmliches lineares Rückgängigmachen.
Sumudu Fernando

Können Sie die Idee dieser ID gegen Zeiger näher erläutern? Sicherlich funktioniert eine Zeiger- / Speicheradresse genauso gut wie eine ID?
Paulm

@paulm: Im Wesentlichen werden die tatsächlichen Daten durch (ID, Version) indiziert. Zeiger beziehen sich auf eine bestimmte Version eines Objekts, aber Sie möchten auf den aktuellen Status eines Objekts verweisen, unabhängig davon, was dies sein mag. Sie möchten es also nach ID und nicht nach (ID, Version) adressieren. Sie können es so umstrukturieren, dass Sie einen Zeiger auf die Tabelle (version => data) speichern und jedes Mal nur die neueste auswählen. Dies kann jedoch die Lokalität beeinträchtigen, wenn Sie Daten beibehalten, Bedenken ein wenig trüben und es schwieriger machen Führen Sie einige häufig gestellte Abfragen durch, sodass dies normalerweise nicht der Fall ist.
Chris Morgan

17

Wenn Sie von GoF sprechen, wird im Memento- Muster speziell das Rückgängigmachen behandelt.


7
Nicht wirklich, dies spricht seinen anfänglichen Ansatz an. Er bittet um einen alternativen Ansatz. Das erste ist das Speichern des vollständigen Zustands für jeden Schritt, während das letztere nur die "Unterschiede" speichert.
Andrei Rînea

15

Wie bereits erwähnt, ist das Befehlsmuster eine sehr leistungsstarke Methode zum Implementieren von Rückgängig / Wiederherstellen. Aber es gibt einen wichtigen Vorteil, den ich im Befehlsmuster erwähnen möchte.

Wenn Sie das Rückgängigmachen / Wiederherstellen mithilfe des Befehlsmusters implementieren, können Sie große Mengen an doppeltem Code vermeiden, indem Sie die an den Daten ausgeführten Vorgänge (bis zu einem gewissen Grad) abstrahieren und diese Vorgänge im Rückgängig- / Wiederherstellungssystem verwenden. Zum Beispiel in einem Texteditor sind Ausschneiden und Einfügen komplementäre Befehle (abgesehen von der Verwaltung der Zwischenablage). Mit anderen Worten, die Rückgängig-Operation für einen Ausschnitt ist Einfügen und die Rückgängig-Operation für eine Einfügung ist Ausschneiden. Dies gilt für viel einfachere Vorgänge wie das Eingeben und Löschen von Text.

Der Schlüssel hier ist, dass Sie Ihr Rückgängig- / Wiederherstellungssystem als primäres Befehlssystem für Ihren Editor verwenden können. Anstatt das System wie "Objekt rückgängig machen, Dokument ändern" zu schreiben, können Sie "Objekt rückgängig machen, Wiederherstellungsvorgang für Objekt rückgängig machen ausführen, um das Dokument zu ändern".

Zugegeben, viele Leute denken sich: "Nun duh, ist das nicht Teil des Punktes des Befehlsmusters?" Ja, aber ich habe zu viele Befehlssysteme gesehen, die zwei Befehlssätze haben, einen für Sofortoperationen und einen für Rückgängig / Wiederherstellen. Ich sage nicht, dass es keine Befehle gibt, die für Sofortoperationen und Rückgängigmachen / Wiederherstellen spezifisch sind, aber das Reduzieren der Duplizierung macht den Code wartbarer.


1
Ich habe nie pasteals cut^ -1 gedacht .
Lenar Hoyt

8

Vielleicht möchten Sie sich für das Rückgängigmachen auf den Paint.NET-Code beziehen - sie haben ein wirklich schönes Rückgängig-System. Es ist wahrscheinlich ein bisschen einfacher als das, was Sie brauchen, aber es könnte Ihnen einige Ideen und Richtlinien geben.

-Adam


4
Eigentlich ist der Paint.NET-Code nicht mehr verfügbar, aber Sie können den gegabelten Code
Igor Brejc

7

Dies kann ein Fall sein, in dem CSLA anwendbar ist. Es wurde entwickelt, um Objekte in Windows Forms-Anwendungen komplex rückgängig zu machen.


6

Ich habe komplexe Rückgängig-Systeme erfolgreich mit dem Memento-Muster implementiert - sehr einfach und hat den Vorteil, dass natürlich auch ein Redo-Framework bereitgestellt wird. Ein subtilerer Vorteil besteht darin, dass aggregierte Aktionen auch in einem einzelnen Rückgängig enthalten sein können.

Kurz gesagt, Sie haben zwei Stapel von Erinnerungsobjekten. Einer für Rückgängig, der andere für Wiederherstellen. Bei jeder Operation wird ein neues Andenken erstellt. Im Idealfall handelt es sich dabei um einige Aufrufe, um den Status Ihres Modells, Dokuments (oder was auch immer) zu ändern. Dies wird dem Rückgängig-Stapel hinzugefügt. Wenn Sie einen Rückgängig-Vorgang ausführen, führen Sie nicht nur die Rückgängig-Aktion für das Memento-Objekt aus, um das Modell wieder zu ändern, sondern entfernen das Objekt auch vom Rückgängig-Stapel und schieben es direkt auf den Redo-Stapel.

Wie die Methode zum Ändern des Status Ihres Dokuments implementiert wird, hängt vollständig von Ihrer Implementierung ab. Wenn Sie einfach einen API-Aufruf durchführen können (z. B. ChangeColour (r, g, b)), stellen Sie ihm eine Abfrage voran, um den entsprechenden Status abzurufen und zu speichern. Das Muster unterstützt aber auch das Erstellen tiefer Kopien, Speicher-Snapshots, die Erstellung temporärer Dateien usw. - es liegt ganz bei Ihnen, da es sich lediglich um eine virtuelle Methodenimplementierung handelt.

Um aggregierte Aktionen auszuführen (z. B. Benutzer-Umschalt - Wählt eine Ladung von Objekten aus, für die eine Operation ausgeführt werden soll, z. B. Löschen, Umbenennen, Ändern von Attributen), erstellt Ihr Code einen neuen Rückgängig-Stapel als einzelnes Andenken und übergibt diesen an die eigentliche Operation an Fügen Sie die einzelnen Operationen hinzu. Ihre Aktionsmethoden müssen also nicht (a) über einen globalen Stapel verfügen, über den Sie sich Sorgen machen müssen, und (b) können gleich codiert werden, unabhängig davon, ob sie isoliert oder als Teil einer aggregierten Operation ausgeführt werden.

Viele Rückgängig-Systeme befinden sich nur im Arbeitsspeicher, aber Sie können den Rückgängig-Stapel auf Wunsch beibehalten, denke ich.


5

Ich habe gerade über das Befehlsmuster in meinem agilen Entwicklungsbuch gelesen - vielleicht hat das Potenzial?

Sie können von jedem Befehl die Befehlsschnittstelle implementieren lassen (die über eine Execute () -Methode verfügt). Wenn Sie rückgängig machen möchten, können Sie eine Rückgängig-Methode hinzufügen.

Mehr Infos hier


4

Ich bin mit Mendelt Siebenga über die Tatsache, dass Sie das Befehlsmuster verwenden sollten. Das von Ihnen verwendete Muster war das Memento-Muster, das mit der Zeit sehr verschwenderisch werden kann und wird.

Da Sie an einer speicherintensiven Anwendung arbeiten, sollten Sie entweder angeben können, wie viel Speicher die Rückgängig-Engine belegen darf, wie viele Rückgängig-Ebenen gespeichert werden oder auf welchem ​​Speicher sie beibehalten werden. Wenn Sie dies nicht tun, werden Sie bald auf Fehler stoßen, die darauf zurückzuführen sind, dass der Computer nicht genügend Speicher hat.

Ich würde Ihnen raten, zu prüfen, ob es ein Framework gibt, das bereits ein Modell für Undos in der Programmiersprache / dem Framework Ihrer Wahl erstellt hat. Es ist schön, neue Dinge zu erfinden, aber es ist besser, etwas zu nehmen, das bereits in realen Szenarien geschrieben, debuggt und getestet wurde. Es wäre hilfreich, wenn Sie hinzufügen würden, in was Sie dies schreiben, damit die Leute Frameworks empfehlen können, die sie kennen.


3

Codeplex-Projekt :

Es ist ein einfaches Framework, mit dem Sie Ihren Anwendungen Rückgängig- / Wiederherstellungsfunktionen hinzufügen können, die auf dem klassischen Befehlsentwurfsmuster basieren. Es unterstützt das Zusammenführen von Aktionen, verschachtelten Transaktionen, die verzögerte Ausführung (Ausführung beim Transaktions-Commit auf oberster Ebene) und den möglichen nichtlinearen Rückgängig-Verlauf (bei dem Sie zwischen mehreren Aktionen wählen können, die wiederholt werden sollen).


2

Die meisten Beispiele, die ich gelesen habe, verwenden entweder den Befehl oder das Erinnerungsmuster. Mit einer einfachen Deque-Struktur können Sie dies aber auch ohne Entwurfsmuster tun .


Was würdest du in die Deque setzen?

In meinem Fall habe ich den aktuellen Status der Vorgänge angegeben, für die ich die Funktion zum Rückgängigmachen / Wiederherstellen wollte. Mit zwei Deques (Rückgängig / Wiederherstellen) mache ich das Rückgängigmachen in der Rückgängig-Warteschlange (Pop First Item) und füge es in die Redo-Dequeue ein. Wenn die Anzahl der Gegenstände in den Warteschlangen die bevorzugte Größe überschreitet, platziere ich einen Gegenstand des Schwanzes.
Patrik Svensson

2
Was Sie beschreiben , tatsächlich IST ein Entwurfsmuster :). Das Problem bei diesem Ansatz besteht darin, dass Ihr Status viel Speicher benötigt - das Behalten mehrerer Dutzend Statusversionen wird dann unpraktisch oder sogar unmöglich.
Igor Brejc

Oder Sie können ein Verschlusspaar speichern, das den normalen Betrieb darstellt, und den Vorgang rückgängig machen.
Akangka

2

Eine clevere Methode zum Rückgängigmachen, die Ihre Software auch für die Zusammenarbeit mit mehreren Benutzern geeignet macht, ist die Implementierung einer operativen Transformation der Datenstruktur.

Dieses Konzept ist nicht sehr beliebt, aber gut definiert und nützlich. Wenn Ihnen die Definition zu abstrakt erscheint, ist dieses Projekt ein erfolgreiches Beispiel dafür, wie eine operative Transformation für JSON-Objekte in Javascript definiert und implementiert wird



1

Wir haben das Laden und Speichern des Serialisierungscodes für "Objekte" für ein bequemes Formular zum Speichern und Wiederherstellen des gesamten Status eines Objekts wiederverwendet. Wir verschieben diese serialisierten Objekte auf den Rückgängig-Stapel - zusammen mit einigen Informationen darüber, welche Operation ausgeführt wurde, und Hinweisen zum Rückgängigmachen dieser Operation, wenn nicht genügend Informationen aus den serialisierten Daten gewonnen wurden. Beim Rückgängigmachen und Wiederherstellen wird häufig nur ein Objekt durch ein anderes ersetzt (theoretisch).

Es gab viele VIELE Fehler aufgrund von Zeigern (C ++) auf Objekte, die nie behoben wurden, als Sie einige ungerade Rückgängig-Wiederherstellungssequenzen ausführten (diese Stellen wurden nicht aktualisiert, um sicherere rückgängig gemachte "Bezeichner" rückgängig zu machen). Bugs in diesem Bereich oft ... ähm ... interessant.

Einige Vorgänge können Sonderfälle für die Geschwindigkeits- / Ressourcennutzung sein - z. B. das Bemessen von Objekten oder das Verschieben von Objekten.

Die Mehrfachauswahl bietet auch einige interessante Komplikationen. Zum Glück hatten wir bereits ein Gruppierungskonzept im Code. Der Kommentar von Kristopher Johnson zu Unterelementen kommt dem, was wir tun, ziemlich nahe.


Dies klingt mit zunehmender Größe Ihres Modells zunehmend unbrauchbar.
Warren P

Inwiefern? Dieser Ansatz funktioniert unverändert weiter, da jedem Objekt neue "Dinge" hinzugefügt werden. Die Leistung könnte ein Problem sein, da die serialisierte Form der Objekte an Größe zunimmt - dies war jedoch kein großes Problem. Das System wird seit über 20 Jahren kontinuierlich weiterentwickelt und von Tausenden von Benutzern verwendet.
Aardvark

1

Ich musste dies tun, als ich einen Löser für ein Peg-Jump-Puzzlespiel schrieb. Ich habe jede Bewegung zu einem Befehlsobjekt gemacht, das genügend Informationen enthält, die entweder ausgeführt oder rückgängig gemacht werden können. In meinem Fall war dies so einfach wie das Speichern der Startposition und der Richtung jeder Bewegung. Ich habe dann alle diese Objekte in einem Stapel gespeichert, damit das Programm beim Zurückverfolgen problemlos so viele Bewegungen rückgängig machen kann, wie es benötigt.


1

Sie können die vorgefertigte Implementierung des Rückgängig / Wiederherstellen-Musters in PostSharp ausprobieren. https://www.postsharp.net/model/undo-redo

Sie können Ihrer Anwendung Funktionen zum Rückgängigmachen / Wiederherstellen hinzufügen, ohne das Muster selbst zu implementieren. Es verwendet das Recordable-Muster, um die Änderungen in Ihrem Modell zu verfolgen, und es funktioniert mit dem INotifyPropertyChanged-Muster, das auch in PostSharp implementiert ist.

Sie erhalten UI-Steuerelemente und können den Namen und die Granularität der einzelnen Vorgänge festlegen.


0

Ich habe einmal an einer Anwendung gearbeitet, in der alle Änderungen, die durch einen Befehl am Modell der Anwendung vorgenommen wurden (z. B. CDocument ... wir haben MFC verwendet), am Ende des Befehls beibehalten wurden, indem Felder in einer internen Datenbank aktualisiert wurden, die im Modell verwaltet wird. Wir mussten also nicht für jede Aktion einen eigenen Rückgängig- / Wiederherstellungscode schreiben. Der Rückgängig-Stapel erinnerte sich bei jeder Änderung eines Datensatzes (am Ende jedes Befehls) einfach an die Primärschlüssel, Feldnamen und alten Werte.


0

Der erste Abschnitt von Entwurfsmuster (GoF, 1994) enthält einen Anwendungsfall zum Implementieren des Rückgängigmachens / Wiederherstellens als Entwurfsmuster.


0

Sie können Ihre ursprüngliche Idee performant machen.

Verwenden Sie persistente Datenstrukturen und führen Sie eine Liste mit Verweisen auf den alten Zustand . (Dies funktioniert jedoch nur dann wirklich, wenn alle Daten in Ihrer Statusklasse unveränderlich sind und alle Operationen eine neue Version zurückgeben. Die neue Version muss jedoch keine tiefe Kopie sein. Ersetzen Sie einfach die Kopie der geänderten Teile -on-write '.)


0

Ich habe festgestellt, dass das Befehlsmuster hier sehr nützlich ist. Anstatt mehrere umgekehrte Befehle zu implementieren, verwende ich Rollback mit verzögerter Ausführung auf einer zweiten Instanz meiner API.

Dieser Ansatz erscheint sinnvoll, wenn Sie einen geringen Implementierungsaufwand und eine einfache Wartbarkeit wünschen (und sich den zusätzlichen Speicher für die 2. Instanz leisten können).

Ein Beispiel finden Sie hier: https://github.com/thilo20/Undo/


-1

Ich weiß nicht, ob dies für Sie von Nutzen sein wird, aber als ich bei einem meiner Projekte etwas Ähnliches tun musste, habe ich UndoEngine von http://www.undomadeeasy.com heruntergeladen - eine wunderbare Engine und es war mir wirklich egal, was sich unter der Motorhaube befand - es funktionierte einfach.


Bitte posten Sie Ihre Kommentare nur als Antwort, wenn Sie sicher sind, Lösungen anbieten zu können! Ansonsten lieber als Kommentar unter der Frage posten! (Wenn dies jetzt nicht möglich ist! Warten Sie bitte, bis Sie einen guten Ruf haben.)
InfantPro'Aravind '11.

-1

Meiner Meinung nach könnte das UNDO / REDO auf zwei Arten allgemein umgesetzt werden. 1. Befehlsebene (als Befehlsebene bezeichnet Rückgängig / Wiederherstellen) 2. Dokumentebene (als globales Rückgängig / Wiederherstellen bezeichnet)

Befehlsebene: Wie viele Antworten zeigen, wird dies mithilfe des Memento-Musters effizient erreicht. Wenn der Befehl auch das Journalisieren der Aktion unterstützt, wird ein Wiederherstellen problemlos unterstützt.

Einschränkung: Sobald der Umfang des Befehls abgelaufen ist, ist das Rückgängigmachen / Wiederherstellen nicht mehr möglich, was zum (globalen) Rückgängigmachen / Wiederherstellen auf Dokumentebene führt

Ich denke, Ihr Fall würde in das globale Rückgängigmachen / Wiederherstellen passen, da er für ein Modell geeignet ist, das viel Speicherplatz benötigt. Dies ist auch geeignet, um auch selektiv rückgängig zu machen / zu wiederholen. Es gibt zwei primitive Typen

  1. Alle Speicher rückgängig machen / wiederholen
  2. Objektebene Wiederherstellen rückgängig machen

In "Alle Speicher rückgängig machen / wiederholen" wird der gesamte Speicher als verbundene Daten (z. B. ein Baum, eine Liste oder ein Diagramm) behandelt, und der Speicher wird von der Anwendung und nicht vom Betriebssystem verwaltet. Daher werden neue und gelöschte Operatoren in C ++ überladen, um spezifischere Strukturen zu enthalten, um Operationen wie a effektiv zu implementieren. Wenn ein Knoten geändert wird, b. Halten und Löschen von Daten usw. Die Funktionsweise besteht im Wesentlichen darin, den gesamten Speicher zu kopieren (vorausgesetzt, die Speicherzuordnung wird bereits von der Anwendung mithilfe fortschrittlicher Algorithmen optimiert und verwaltet) und in einem Stapel zu speichern. Wenn die Kopie des Speichers angefordert wird, wird die Baumstruktur basierend auf der Notwendigkeit einer flachen oder tiefen Kopie kopiert. Eine tiefe Kopie wird nur für die Variable erstellt, die geändert wird. Da jede Variable mithilfe einer benutzerdefinierten Zuordnung zugewiesen wird, Die Anwendung hat das letzte Wort, wann sie bei Bedarf gelöscht werden muss. Die Dinge werden sehr interessant, wenn wir das Rückgängigmachen / Wiederherstellen partitionieren müssen, wenn es passiert, dass wir eine Reihe von Operationen programmgesteuert und selektiv rückgängig machen / wiederholen müssen. In diesem Fall erhalten nur diese neuen Variablen oder gelöschten Variablen oder geänderten Variablen ein Flag, sodass Rückgängig / Wiederherstellen nur diesen Speicher rückgängig macht / wiederherstellt. Die Dinge werden noch interessanter, wenn wir ein teilweises Rückgängigmachen / Wiederherstellen innerhalb eines Objekts durchführen müssen. In diesem Fall wird eine neuere Idee des "Besuchermusters" verwendet. Es heißt "Object Level Undo / Redo" oder gelöschte Variablen oder geänderte Variablen erhalten ein Flag, so dass Rückgängig / Wiederherstellen nur diesen Speicher rückgängig macht / wiederherstellt. Die Dinge werden noch interessanter, wenn wir ein teilweises Rückgängigmachen / Wiederherstellen innerhalb eines Objekts durchführen müssen. In diesem Fall wird eine neuere Idee des "Besuchermusters" verwendet. Es heißt "Object Level Undo / Redo" oder gelöschte Variablen oder geänderte Variablen erhalten ein Flag, so dass Rückgängig / Wiederherstellen nur diesen Speicher rückgängig macht / wiederherstellt. Die Dinge werden noch interessanter, wenn wir ein teilweises Rückgängigmachen / Wiederherstellen innerhalb eines Objekts durchführen müssen. In diesem Fall wird eine neuere Idee des "Besuchermusters" verwendet. Es heißt "Object Level Undo / Redo"

  1. Objektebene Rückgängig / Wiederherstellen: Wenn die Benachrichtigung zum Rückgängigmachen / Wiederherstellen aufgerufen wird, implementiert jedes Objekt eine Streaming-Operation, bei der der Streamer vom Programm die alten Daten / neuen Daten erhält, die programmiert sind. Die Daten, die nicht gestört werden, bleiben ungestört. Jedes Objekt erhält einen Streamer als Argument und innerhalb des UNDo / Redo-Aufrufs werden die Daten des Objekts gestreamt / entströmt.

Sowohl 1 als auch 2 können Methoden wie 1. BeforeUndo () 2. AfterUndo () 3. BeforeRedo () 4. AfterRedo () haben. Diese Methoden müssen im grundlegenden Befehl "Rückgängig / Wiederherstellen" (nicht im Kontextbefehl) veröffentlicht werden, damit alle Objekte diese Methoden ebenfalls implementieren, um eine bestimmte Aktion zu erhalten.

Eine gute Strategie besteht darin, eine Mischung aus 1 und 2 zu erstellen. Das Schöne ist, dass diese Methoden (1 und 2) selbst Befehlsmuster verwenden

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.