# Best Practices für Undo/Redo



## Marco13 (24. Sep 2012)

Hallo

Ich bastle gerade an einem Editor (vereinfacht gesagt: Für etwas, was aus Knoten und Kanten besteht) und wollte dabei zum ersten mal eine richtige Unterstützung für Undo/Redo einbauen. Die grundlegenden Konzepte mit UndoManager und UndoableEdit sind ja recht einleuchtend. Was mich aber doch (mehr als erwartet) irritiert ist der ungeheure Planungsaufwand. DASS es aufwändig ist, war mir klar, und auch dass es praktisch unmöglich ist, so etwas _nachträglich_ in ein bestehendes System einzubauen. Ich merke jetzt aber, dass ich viele Dinge nur für das Undo/Redo komplett anders mache, als ich es eigentlich machen würde. Die ganzen Schnittstellen scheinen sich immer mehr an dem Ziel zu orientieren, alles mit Undo/Redo behandeln zu können, und werden dadurch irgendwie un-intuitiv... 

Kennt jemand gute Beispiele dazu, wie man auch kompliziertere Anwendungen mit Undo/Redo aufbaut? Die Dinge, die man mit schnellen Websuchen findet, beziehen sich meistens auf irgendwelche Trivialbeispiele. Insbesondere habe ich noch kein vernünftiges Beispiel gefunden, das zeigt, wie man sinnvoll mehrere Edits zu einem größeren Edit gruppieren kann. Der Versuch, aus den bestehenden Implementierungen z.B. rund um 'AbstractDocmument' herum schlau zu werden scheint ein ungünstiges Verhältnis zwischen Quellcode-Scrollen und Aha-Effekten zu haben...

bye


----------



## Firephoenix (24. Sep 2012)

Wie wäre es denn mit einem Ansatz per Command-Pattern?
Wenn jeder command ein undo() implementieren muss ist es dann sogar extrem leicht ausgeführte Befehle zu speichern oder zu gruppieren, bzw auch als Gruppe rückgängig zu machen.

[EDIT]Die 2 von dir angesprochenen Klassen scheinen aber schon stark in die Richtung zu gehen, ein besserer ansatz als ein angepasstes Designpattern fällt mir aber auch nicht ein.[/EDIT]

Gruß


----------



## F.S.WhiTeY (24. Sep 2012)

> Wenn jeder command ein undo() implementieren muss ist es dann sogar extrem leicht ausgeführte Befehle zu speichern oder zu gruppieren, bzw auch als Gruppe rückgängig zu machen.



Richtig! Man könnte das dann sogar als eine Art doppelt verkettete Liste implementieren (oder eine solche nutzen), was die vor- und rückwärts navigation erleichtern würde.


----------



## Gast2 (24. Sep 2012)

Schau dir mal EMF an, das bringt das schon vorgefertigt mit.

Hier sind eventuell noch weitere Vorteile gegenüber Pojos. Ziemlich in der Mitte von der Präsentation(schon älter)
http://www.java-forum-stuttgart.de/jfs/2006/folien/D2_Gerhardt_Moroff_Eberle.pdf


----------



## Marco13 (24. Sep 2012)

Hmja, über das Command Pattern stolpert man da natürlich. Es ist recht nahe liegend, und die existierenden Klassen (UndoManager & Co) haben schon Ähnlichkeiten dazu (im Prinzip kann man mit UndoableEdits auch ziemlich genau das Command Pattern in seiner einfachsten Form nachbauen). 

Ich dachte auch kurz, dass es vielleicht einfacher sein könnte, das ganze from scratch minimalistisch mit eigenem Command-Pattern selbst zu schreiben. Einige der Konzepte sind wohl in beiden Fällen gewöhnungsbedürftig...

Aber es gibt auch einige Fragen, die sich in beiden Fällen stellen. Eine ist die Granularität. An einem sehr suggestiven Beispiel: Es geht, wie gesagt, um Knoten und Kanten. Nun würde man eigentlich Methoden anbieten, mit denen man
- einen Knoten selektieren kann
- einen Knoten DEselektieren kann
- eine Kante selektieren kann
- eine Kante DEselektieren kann
Wenn man nun Untersützung für Undo/Redo will, tut es da eben kein "setNodeSelected(boolean)" mehr, sondern man braucht ein Command (oder UndoableEdit - ich verwende die jetzt mal Synonym). Für jede einzene Operation? Ja, vermutlich. Und wenn man (mit einem Auswahlrechteck) 20 Knoten gleichzeitig auswählt, werden dann 20 Commands für die Auswahl eines einzelnen Knotens erstellt? (Und ggf. zusammengefasst?) Oder macht man ein Command, mit dem man eine _Menge_ von Knoten auswählt? Und wenn mit derselben Benutzeraktion auch 40 Knoten DEselektiert wurden? Und das ganze auch für Kanten gilt? Ich bin da am Schwanken, und nicht sicher, wie die Struktur am günstigsten ist. Es reicht vom hyper-feingranularen

```
class NodeSelectionCommand implements Command 
{
    private Node node;
    void undo() { editor.setNodeSelected(node, false); }
    void redo() { editor.setNodeSelected(node, true); }
}
class NodeDeSelectionCommand implements Command ...
class EdgeSelectionCommand implements Command ...
class EdgeDeSelectionCommand implements Command ...
...
```
wo man ggf. sehr viele Commands auf unterschiedlichste Weisen gruppieren müßte, bis zum Schweizer Taschenmesser der Selektion 

```
class SelectionCommand {
    private Set<Node> nodesAdded;
    private Set<Node> nodesRemoved;
    private Set<Edge> edgesAdded;
    private Set<Edge> edgesRemoved;
...
```
wo ich im Moment bin, mit einem etwas schwummrigen Gefühl im Bauch. (Das ganze auch unter Berücksichtigung der Tatsache, dass jedes Ausführen eines Commands auch die Benachrichtigung von Listenern zur Folge hat, und da so EIN "mächtigeres" Command vielleicht sinnvoller wäre...)


Zusätzlich bin ich mir bei einigen Verantworklichkeiten nicht ganz sicher. Wenn man einen ausgewählten Knoten löscht, dann ist er danach nicht mehr selektiert. Wer ist dafür verantwortlich, die Selektion aufzuheben? Wenn man das Löschen rückgängig machen will, muss das Lösch-Command ja auf jeden Fall über die vorherige Selektion bescheid wissen. Wird das mit einem Unter-Command gemacht...

```
class DeleteCommand implements Command {
    private NodeDeSelectionCommand deSelection; // may be null...
    ...
}
```
!? Spätestens wenn man von 100 Knoten 50 auswählt und davon dann 20 gelöscht werden, und dadurch vielleicht 10 Kanten wegfallen von denen 5 ausgewählt waren, wird das ja ziemlich kompliziert... ???:L Es scheint mir eben, als müßte man seine ganzen Schnittstellen und angebotenen Operationen darauf ausrichten, möglichst leicht in Commands/UndoableEdits umwandelbar zu sein (und was "möglichst leicht" ist, da taste ich mich gerade erst ran...)


----------



## Gast2 (24. Sep 2012)

Du machst einzlene Commands und wenn du mehrere durchführen willst machst du ein CompboundCommand welches eine Liste aller anderen Commands hat. Das kann man alles generisch halten.
z.B.
CompoundCommand (EMF Javadoc)

Wenn du "nur" setter aufrufst musst nur immer den alten Wert speichern und den bei undo setzen und bei redo wieder den neuen wert.
Bei so einem "SetCommand" kannst viel mit Reflection machen. z.B.
SetCommand (EMF Javadoc)

Wie gesagt es gibt vieles schon, vielleicht kannst du davon einiges verwenden.


----------



## Marco13 (24. Sep 2012)

Meine Antwort und ein Hinweis auf EMF haben sich überschnitten. Ich hatte schon gelesen, dass es im RCP-Umfeld da einiges geben soll (z.B. auch in JFace). Das von EMF sieht auf den ersten Blick strukturell sehr ähnlich (fast gleich) aus wie UndoableEdit & Co. Verwenden kann man da sicher das eine oder andere, aber über die oben angedeuteten Punkte bin ich mir trotzdem noch nicht ganz im klaren. Es erscheint etwas ungewohnt... einerseits dass alle Änderungen am Modellzustand über Commands abgebildet werden müssen, und andererseits dass das wirklich erschöpfend sein muss - also im algebraischen Sinn vollständig und abgeschlossen. Schon eine vermeintliche Kleinigkeit wie...

```
void removeNode(Node node)
{
    nodes.remove(node);
    selectedNodes.remove(node); // ... ein beiläufiges un-selektieren (egal ob er selektiert war oder nicht!)
    ...
```
macht ja sämtliche Undo/Redo-Ziele kaputt... vom dort offensichtlich noch fehlenden

```
for (Edge edge : edgesThatAreConnectedTo(node))
    {
        removeEdge(edge);
    }
}
```
und dem, was DAS dann wiederum für Auswirkunge hat, mal ganz abgesehen  Insgesamt muss man anscheinend ziemlich viel Wissen über das Modell in die Undo/Redo-Funktionalität stecken - und dann noch aufpassen, dass das beides nicht inkonsistent wird...


----------



## Gast2 (25. Sep 2012)

Marco13 hat gesagt.:


> Meine Antwort und ein Hinweis auf EMF haben sich überschnitten. Ich hatte schon gelesen, dass es im RCP-Umfeld da einiges geben soll (z.B. auch in JFace).


Nein EMF ist davon vollkommen losgekoppelt.

EMF bietet auf Modelebene Undo/Redo an  d.h. du rufst einfach einem ChangeCommand deine Modell Änderungen auf und musst das undo/redo dazu nicht mehr implementieren. Hat nichts mit der UI oder sonstigem zu tun.

Ich verstehe noch nicht ganz was du mit deinem Selektieren/Deselektieren erreichen willst? Habe ich noch nie gesehen dass es dafür Undo/Redo gibt, kenn ich zumindest keinen UseCase oder eine sonstige Anwendung. 
Das wäre ein undo/redo auf einen UI zustand. Aber ein undo/redo macht man meistens wenn sich was geändert hat (Model) und die Änderung ruckgängig machen will.


Klar ist jede Modeländerung muss dann über ein Command gehen.


----------



## Spacerat (25. Sep 2012)

Was meinst du eigentlich mit Verantwortlichkeiten? Es gibt diese Commands und das Objekt an dem sie ausgeführt werden. Sinnvollerweise speichert man die einzelnen Commands mit allen zugehörigen Daten in der Reihenfolge, in der man sie ausführt. Undo führt man dann vom Ende der Liste bis zum Anfang aus und Redo halt in die Richtung, in der man sie zuvor ausgeführt hat. So kann es eigentlich kaum passieren, dass plötzlich etwas selektiert wird, was zuvor gelöscht wurde. Deswegen müssen sich diese Commands untereinander auch nicht über Listener verständigen bzw. durch das Objekt über Änderungen informiert werden. Erst wenn man so eine Liste als Makro implementiert, können zwischendrin Schritte entfernt oder hinzugefügt werden, die einzelnen Commands solcher Makros sollten dann aber zumindest Exceptions werfen, wenn etwas nicht stimmt (z.B. bei Selektierung nicht vorhandenen Inhalts).
BTW.: Listener heisst Zuhörer und nicht Täter. Das bedeutet, dass Listener höchstens auf Änderungen reagieren, jedoch nie versuchen sollten etwas Rückgängig zu machen.
@SirWayne: Den Usecase hat man z.B. bei 3D-Editoren (C4D, 3DS usw.). Wenn man z.B. viele dicht beieinander liegende Punkte oder Kanten mit einer Art "Pinsel" gleichzeitig selektieren will und sich nach einer bereits heiden Arbeit dabei mal kurz verhaspelt.


----------



## bygones (25. Sep 2012)

ich wuerde nicht jegliche Aktion/Command in deinem Editor dann ein neues Interface oder sonstiges aufbrummen die dann beschreiben wie undo/redo geht. 

Du brauchst im Grunde ja nur einen Manager der Events verwaltet, ein Event kann redo/undo und alle Commands die eben undo/redo unterstuetzen sollen feuern bei einer Aktion ein solches Event. Der Manager nimmt diese dann auf und kann diese dann eben undo/redo-en.

was ist denn so besonders dass bei einer selektieren man undo/redo machen will ? Ich wuerde intuitiv dies nicht also solche Aktion sehen.

Generell wuerde ich es so sagen:

Jede Aktion die undo/redo unterstuetzen soll feuert ein Event mit den Infos wie dies geschieht. Der Manager sammelt diese und ueber ihn kann dann die eigentlich undo/redo aktion ausgefuehrt werden.

Muessen Aktionen gesammelt werden um ein "atomares" Undo/redo zu erlauben, so helfen Strukturen wie javax.swing.undo.CompoundEdit (allg siehe javax.swing.undo package).

Keine Ahnung ob und wie EMF das kann, einfach macht oder so.



Marco13 hat gesagt.:


> und dem, was DAS dann wiederum für Auswirkunge hat, mal ganz abgesehen  Insgesamt muss man anscheinend ziemlich viel Wissen über das Modell in die Undo/Redo-Funktionalität stecken - und dann noch aufpassen, dass das beides nicht inkonsistent wird...


genau gesehen braucht die Undo/Redo Funktionalitaet an sich kein Wissen ueber das Modell, wann man wie welche Events feuert bedarf etwas wissen, aber auch das find ich nicht so schlimm, wenn man granulare Aktionen hat und dann eben per Gruppierung diese zusammenbringt


----------



## Gast2 (25. Sep 2012)

bygones hat gesagt.:


> Keine Ahnung ob und wie EMF das kann, einfach macht oder so.



EMF hat ein CommandStack auf dem du Command ausführst. 
CommandStack (EMF Javadoc)

Und wie gesagt gibt es schon einiges Commands dazu.
org.eclipse.emf.edit.command (EMF Javadoc)

Am einfachsten ist man benutzt das ChangeCommand das kann man dann alles an seinem Model ändern. Durch die typisierte Reflection API und eigene Notfication Framework kann EMF selber seine undo/redo Aktionen bauen.




Spacerat hat gesagt.:


> @SirWayne: Den Usecase hat man z.B. bei 3D-Editoren (C4D, 3DS usw.). Wenn man z.B. viele dicht beieinander liegende Punkte oder Kanten mit einer Art "Pinsel" gleichzeitig selektieren will und sich nach einer bereits heiden Arbeit dabei mal kurz verhaspelt.



Okay trotzdem sehe ich das Problem von Marco nicht =)


----------



## Spacerat (25. Sep 2012)

@bygones: Un-/Redo sind keine Aktionen, die irgendwo unterstützt werden müssen, für dahinführende Aktivitäten Events eindeutig die falsche Wahl. Das bedeutet nicht, dass bei Änderungen keine Events gefeuert werden dürfen.
Mal ein simples Beispiel, womit man z.B. "<BufferedImage>.setRGB()" (eine Methode von der wir wissen, dass sie keine "verwendbaren" Events feuert) aufzeichnen, rückgangig machen und rückgängig machen widerrufen kann.

```
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;


public class MakroImage {
	private final List<Command<?>> steps = new ArrayList<>();
	private final BufferedImage toWorkOn;
	private int step = -1, size;

	public MakroImage(BufferedImage toWorkOn) {
		if(toWorkOn == null) {
			throw new NullPointerException();
		}
		this.toWorkOn = toWorkOn;
	}

	public int setRGB(int x, int y, int rgb) {
		Command<Integer> c = new Command<Integer>() {
			private int savedColor, setColor, x , y;

			@Override
			public Integer execute(Object ... args) {
				if(args.length < 3) {
					throw new RuntimeException("invalid number of arguments");
				}
				for(Object o : args) {
					if(!(o instanceof Integer)) {
						throw new RuntimeException("invalid type");
					}
				}
				x = (Integer) args[0];
				y = (Integer) args[1];
				setColor = (Integer) args[2];
				savedColor = toWorkOn.getRGB(x, y);
				redo();
				return savedColor;
			}

			@Override
			public void undo() {
				toWorkOn.setRGB(x, y, savedColor);
			}

			@Override
			public void redo() {
				toWorkOn.setRGB(x, y, setColor);
			}
		};
		if(step < size) {
			steps.add(c);
			size = steps.size();
			step = size - 1;
		} else {
			steps.set(step++, c);
			steps.set(step, null);
			size = step;
		}
		return c.execute(x, y, rgb);
	}

	public void undo() {
		if(step < 0) {
			return;
		}
		steps.get(step--).undo();
	}

	public void redo() {
		if(step >= size) {
			return;
		}
		steps.get(step++).redo();
	}
}

interface Command<T> {
	T execute(Object ... args);
	void undo();
	void redo();
}
```
Den Code hab' ich mal ungetestet gepostet, kann sein, dass ich mich evtl. bei den Zählern "size" und "step" oder gar beim "Stack" allgemein verhauhen hab', aber Hauptsache, das Prinzip wird deutlich.



SirWayne hat gesagt.:


> Okay trotzdem sehe ich das Problem von Marco nicht =)


Ich auch nicht. Könnte daran liegen, dass es nicht wirklich eines gibt, wo er eines sieht. 


			
				Spacerat hat gesagt.:
			
		

> So kann es eigentlich kaum passieren, dass... usw.


----------



## homer65 (25. Sep 2012)

Habe sowas in meinen IconEditor eingebaut. Der zugehörige Quelltext ist im IconEditor.jar, was von der Download Seite von MyOggRadio runtergeladen werden kann.
Wenn ich mich recht erinnere, funktioniert das in etwa so:
Das zu editierende ist ein Icon. Alles was ein Icon verändert muß das Interface Operation implementieren. Dann speichert man jede Operation in eine ArrayList. Um nun Undo oder Redo zu machen braucht man nichts als das zu Anfang gesicherte Icon und die Liste mit den Operation.


----------



## Marco13 (25. Sep 2012)

Spacerat hat gesagt.:


> Was meinst du eigentlich mit Verantwortlichkeiten?



Nun, der Fall dass man etwas löscht und dabei die Selektion dieses Objektes aufgehoben werden muss, war eben so ein Beispiel. Die durchgeführte Aktion ist "Delete". Also erstellt man ein DeleteCommand, und das ruft 
model.delete(node);
auf. IN dieser Methode wird aber z.B. auch die Selektion aufgehoben. Wenn man das Delete rückgänig macht, z.B. durch
model.add(node);
dann ist der Knoten danach nicht ausgewählt. Das DeleteCommand müßte also auch über die Selektion bescheid wissen. Wäre das dann

```
void execute() {
    if (model.isSelected(node)) {
        // Wird bei model.remove intern automatisch gemacht,
        // aber damit man es rückgängig machen kann...:
        subCommand = createSubCommandToUnselect(node);
    }
    model.remove(node);
}
void undo() {
    model.remove(node);
    if (sumCommand != null) subCommand.undo();
}
```
? Aber üblicherweise ist "model" nur ein Interface, und man weiß im Zweifelsfall nicht, was bei einem "remove(node)" alles NOCH passiert....

Vielleicht geht es nur um die allgemeine (SEHR offensichtliche, aber doch schwierig zu beantwortende) Frage, wie man den Systemzustand "kapselt", und sicherstellt, dass ALLE (auch indirekten) Folgen einer Aktion rückgängig gemacht werden. Ab einer bestimmten Komplexität kommt sowas wie Memento IMHO auch nicht mehr in Frage. Man bewegt sich durch die Methodenaufrufe u.U. durch einen sehr komplexen Zustandsraum. 

@bygones: Ich kann mir nicht vorstellen, dass dieses Kapseln und Konservieren des Systemzustandes möglich ist, NUR indem man auf Events hört. Spacerat hat da ja ein Beispiel gepostet. 

@Spacerat Vielleicht sehe ich (mal wieder) ein Problem, wo keines ist  Ich will das ganze aber nicht zuuu sehr problematisieren, man findet da sicher eine Lösung, und vermutlich muss ich es nur ein, zwei mal "falsch" implementieren, um zu sehen, wie es richtig gegangen wäre. 
Ich hatte ja auch nur nach Best Practices gefragt, bzw. nach Beispielen, die über solche trivialen wie deins ( :bae: ) hinausgehen. Dass man ein "list.add(x)" oder "image.setRGB(...)" leicht rückgängig machen kann, ist klar. Aber stell dir vor, du würdest jetzt in die BufferedImage-Doku schauen, und dort eine Methode sehen wie
int getLastSetRGB(...)
die die zuletzt mit setRGB gesetzte Farbe zurückliefert: Dann wäre dein Undo schon "kaputt" bzw. unzureichend, und im schlimmsten Fall könnte so eine Aktion dann gar nicht rückgängig gemacht werden. Aber das liegt dann wohl in der Verantwortung desjenigen, der das Modell entwirft. Die einzig sinnvolle "übergeordnete" Gegenmaßnahme, die mir demnach jetzt einfallen würde, wäre, das gesamte Modell explizit auf undo/redo auszurichten, indem man ganz gezielt die Methoden entsprechend "paarweise" anbietet, aber das könnte im Einzelfall dann doch schwierig werden...


----------



## bygones (25. Sep 2012)

die Wahl des Worts "Events" war anscheinend falsch... 

@Spacerat:
dein makroImage ist jetzt doch alles... manager, ausfuehrer, redo/undo verantwortlicher ?

ich geh mal davon aus dass es nur ein einfaches bsp gehalten ist...



Marco13 hat gesagt.:


> Aber stell dir vor, du würdest jetzt in die BufferedImage-Doku schauen, und dort eine Methode sehen wie
> int getLastSetRGB(...)
> die die zuletzt mit setRGB gesetzte Farbe zurückliefert: Dann wäre dein Undo schon "kaputt" bzw. unzureichend, und im schlimmsten Fall könnte so eine Aktion dann gar nicht rückgängig gemacht werden.


was ist daran schwerer als bei dem geposteten Bsp... man muesste sich halt nur die letzte Farbe immer merken. Mehr nicht. Redo/undo rufen dann entsprechend die setRGB oder was ich immer auf und speichern sich wieder die alte Farbe.

du musst auch dein Model nicht daraufauslegen und "gepaarte" Methoden anbieten. Wie gesagt, es gibt zb in swing modelle fuer das gruppieren solcher Commands (genauso gemischer begriff)


----------



## Spacerat (25. Sep 2012)

Mal 'ne andere Frage: Woher weis das Model eigentlich, dass eine Node selektiert wurde? Speichert er selektierte Nodes in einer separaten Liste, dann hättest du ein Problem. Setzt du stattdessen in der Node ein Selected-Signal (bzw. Flag), lassen sich auch selektierte Nodes problemlos entfernen und auch wieder hinzufügen. Das Command muss ja den Zustand der einzelnen Nodes gar nicht ändern.

Mein Beispiel sollte aber überall funktionieren, auch bei weniger trivialen Dingen. Sofern der Mechanismus im Model also selektierte Nodes in einer separaten Liste speichert, muss man dieses bei der Ausführung des Commands halt beachten und es genauso machen. Und das ist genau der Punkt, wenn man einem Objekt Rollback-Funktionalität beibringen will. Man muss wissen wie die Methoden arbeiten bzw. wie man an die Information Zustand-Alt kommt und diese so speichert, dass man sie leicht widerherstellen kann.

@bygones: Jep, das MakroImage würde im Prinzip eine komplette Kapselung eines BI's ergeben, wenn man's weiter ausrollt. Evtl. könnte man es sogar erweitern und jeder einzelnen Methode diese Funktionalität beibiegen.


----------



## Marco13 (25. Sep 2012)

@bygones: _was ist daran schwerer als bei dem geposteten Bsp... man muesste sich halt nur die letzte Farbe immer merken. Mehr nicht. Redo/undo rufen dann entsprechend die setRGB oder was ich immer auf und speichern sich wieder die alte Farbe._

Ja, wenn man das rückgängigmachen durch einen Aufruf von setRGB(oldColor) erledigen kann, stimmt das - daher war das als Beispiel unzureichend. Jetzt zu sagen, dass das, was ich eigentlich meinte, auftreten würde, wenn es eine Methode gäbe wie 
List<Integer> getAllArgumentsThatHaveBeenPassedToSetRGBUntilNow()
würde vielleicht zu konstruiert wirken ... mal schauen ob mir noch was praxisnäheres einfällt 


@Spacerat: _Mal 'ne andere Frage: Woher weis das Model eigentlich, dass eine Node selektiert wurde?_

Es gibt einmal das reine "Datenmodell", das über die Selektion nichts weiß. Wenn man die Selektion aber rückgängig machen können will, muss es eine Modellhafte Repräsentation des Selektionszustandes geben (grob wie ein ListSelectionModel in Swing). Dieses Modell enthält z.B. auch die Positionen von Knoten auf dem Bildschirm, was ja auch nicht unbedingt mit einer reinen Daten_struktur_ zu tun hat, sodern nur mit ihrere Präsentation (und z.B. zum Rückgängigmachen von Verschiebungen notwendig ist).  

_Man muss wissen wie die Methoden arbeiten bzw. wie man an die Information Zustand-Alt kommt und diese so speichert, dass man sie leicht widerherstellen kann._

Ja, das meinte ich mit der Aussage, dass man u.U. viele Innereien seines Modells exponieren muss. Die Zustandsübergänge müssen eindeutig und umkehrbar sein, und u.U. sind die ja recht kompliziert, und teilweise eigentlich NUR Sache des Modells. Das Wissen darüber, wie ein Modell-Methodenaufruf rückgängig gemacht werden kann herauszukristallisieren, und das Modell so zu gestalten, dass man dieses Wissen tatsächlich zum Rückgängigmachen _nutzen_ kann, ist (soweit ich das bisher überblicke) nicht durch ein reines "Einwickeln von Methodenaufrufen in Commands" getan, sondern wirklich eine weitere _Dimension_ bei der Planung und Spezifikation der Modellschnittstelle. Nicht unmöglich, und IMHO sogar recht spannend, aber kann schon aufwändig sein....


----------

