1 Einführung in die Programmierung Bertrand Meyer Letzte Bearbeitung 20. Januar 2004 2 Vorlesung 23: Ein Beispiel: Un-Re Das Problem 3 Den Benutzern eines interaktiven Systems ermöglichen, den Effekt des letzten Befehls aufzuheben. Häufig als Control-Z implementiert Sollte mehrstufiges Un-Re ohne Limitierung ausser einem vom Benutzer gesetzten Maximum unterstützen 1
Ein Texteditor 4 Begriff der aktuellen Zeile. Mögliche Befehle wie: Zeile unter aktueller Position einfügen Zeile über aktueller Position einfügen Aktuelle Zeile löschen Aktuelle Zeile ersetzen Aktuelle Zeile mit nächster vertauschen, falls diese existiert Diese Sicht ist zur Vereinfachung zeilenorientiert, aber die Diskussion gilt auch für komplexere Sichten. Zugrunde liege Klasse (aus business model ) 5 class EDIT_CONTROLLER feature text: LINKED_LIST [STRING] remove is -- Remove line at current position. not off text.remove put_right (line: STRING) is -- Insert line after current position. not after text.put_right (line) Hauptschritt beim Finden einer Software- Architektur 6 Die richtigen Abstraktionen finden (Die involvierten Typen von Objekten.) Hier: Die Idee des command 2
History der Session behalten 7 Die History-Liste: Ältestes Neustes history: LINKED_LIST [COMMAND] Was ist ein command -Objekt? 8 Ein Command-Objekt (Instanz der Klasse COMMAND) enthält genüg Informationen über eine einzelne Ausführung eines Befehls durch den Benutzer, um: den Befehl auszuführen den Befehl aufzuheben, falls später verlangt In einem Delete-Befehl, zum Beispiel (wie von remove implementiert), brauchen wir: Die Position der zu löschen Zeile Den Inhalt dieser Zeile! Eine allgemeine Idee des Command 9 deferred class COMMAND feature execute is -- Carry out one execution of this command. deferred ensure ne: ne un is -- Cancel an earlier execution of this command. already: ne deferred 3
Eine allgemeine Idee des Command 10 deferred class COMMAND feature ne: BOOLEAN is -- Has this command been executed? execute is -- Carry out one execution of this command. un is -- Cancel an earlier execution of this command. deferred ensure ne: ne already: ne deferred Command Klassenhierarchie 11 execute un* COMMAND deferred + effective + DELETION execute + un + line index + INSERTION execute + un + index Eine Command-Klasse (Sketch, keine Contracts) class DELETION inherit COMMAND feature controller: EDIT_CONTROLLER -- Access to business model line: STRING -- The line being deleted index: INTEGER -- Position of line being deleted execute is -- Remove current line and remember it. line := controller.item ; index := controller.index controller.remove ; ne := True un is -- Re-insert previously removed line. controller.go_ith (index) controller.put_left (line) 12 4
Zugrunde liege Klasse (vom business model ) class EDIT_CONTROLLER feature text: LINKED_LIST [STRING] remove is -- Remove line at current position. not off text.remove put_right (line: STRING) is -- Insert line after current position. not after text.put_right (line) Also item, index, go_ith, put_left 13 Einen Befehl ausführen 14 Die History-Liste: Ältestes Neustes history: LINKED_LIST [COMMAND] Einen Befehl ausführen 15 decode_user_request if Request is normal command then -- Create command object c corresponding to user request history.ext (c) c.execute elseif Request is UNDO then if not history.before then history.item.un history.back -- We ignore excessive requests elseif Request is REDO then if not history.is_last then history.forth history. item.un -- We ignore excessive requests 5
Bedingte Kreation (1) 16 B A C a1: A if condition_1 then -- Create a1 as an instance of B elseif condition_2 then -- Create a1 as an instance of C etc D a1: A; b1: B; c1: C; d1: D; if condition_1 then create b1.make () a1 := b1 elseif condition_2 then create c1.make () a1 := c1 etc Bedingte Kreation (2) 17 B A C a1: A if condition_1 then -- Create a1 as an instance of B elseif condition_2 then -- Create a1 as an instance of C etc D a1: A if condition_1 then create {B} a1.make () elseif condition_2 then create {C} a1.make () etc Einen Befehl ausführen 18 decode_user_request if Request is normal command then -- Create command object c corresponding to user request history.ext (c) c.execute elseif Request is UNDO then if not history.before then history.item.un history.back -- Ignore excessive requests elseif Request is REDO then if not history.is_last then history.forth history. item.un -- Ignore excessive requests 6
Command-Objekte kreieren (1) 19 c: COMMAND decode_user_request if Request is delete then create {DELETION} c elseif Request is insert then create {INSERTION} c etc Command-Klassenhierarchie 20 execute un* COMMAND deferred + effective + DELETION execute + un + line index + INSERTION execute + un + index Command-Objekte kreieren (2) 21 Jedem Command-Typ eine Nummer (oder sonstigen Schlüssel) geben Eine Tabelle (z.b. ein Array) anfangs mit einer Instanz jeden Command-Typs füllen. Für ein neues Command-Object: Insertion Deletion command_type bestimmen c := clone (COMMAND_TABLE.item (command_type)) 7
Das Un-Re-Pattern 22 Wird häufig benutzt (z.b. in Eiffel-Tools) Ziemlich einfach zu implementieren Elegante Verwung von O-O-Techniken Nachteil: Explosion von kleinen Klassen In Java kann man innere Klassen benutzen. Benutzung von agents 23 Zwei Routinen für jede Befehl: Die Routine um ihn auszuführen Die Routine um ihn aufzuheben! Die History-Liste im Un-Re-Pattern 24 history: LINKED_LIST [COMMAND] Ältestes Neustes 8
Die History-Liste mit agents Die History-Liste wird einfach eine Liste von agent-paaren: history: LINKED_LIST [TUPLE [PROCEDURE [ANY, TUPLE], PROCEDURE [ANY, TUPLE]] 25 De-Insert De-Insert Re-insert De-Insert Prinzip bleibt das gleiche, aber keine Command-Objekte mehr nötig; wir speichern einfach agents. Diese Technik wird in allen aktuellen Eiffel-Tools verwet. Einen Befehl ausführen (vorher) 26 decode_user_request if Request is normal command then -- Create command object c corresponding to user request history.ext (c) c.execute elseif Request is UNDO then if not history.before then history.item.un history.back -- Ignore excessive requests elseif Request is REDO then if not history.is_last then history.forth history. item.un -- Ignore excessive requests Einen Befehl ausführen (jetzt) 27 Decode user_request giving two agents _it and un_it if Request is normal command then history.ext ([_it, un_it]) _it.call ([]) elseif Request is UNDO then if not history.before then history.item.item (2).call ([]) history.back De- De- Reinsert Insert Insert elseif Request is REDO then if not history.is_last then history.forth history.item.item (1).call ([]) Insert Insert Remove 9
28 Ende Vorlesung 23 10