1 / 33

Einführung in die Programmierung Introduction to Programming Prof. Dr. Bertrand Meyer

Einführung in die Programmierung Introduction to Programming Prof. Dr. Bertrand Meyer. Lecture 18: Undo/Redo. Further reference. Chapter 21 of Bertrand Meyer, Object-Oriented Software Construction , Prentice Hall, 1997

kennan
Download Presentation

Einführung in die Programmierung Introduction to Programming Prof. Dr. Bertrand Meyer

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Einführung in die ProgrammierungIntroduction to ProgrammingProf. Dr. Bertrand Meyer Lecture 18: Undo/Redo

  2. Further reference • Chapter 21 of Bertrand Meyer, Object-Oriented Software Construction, Prentice Hall, 1997 • Erich Gamma et al., Design Patterns, Addison –Wesley, 1995: “Command pattern”

  3. The problem • Enabling users of an interactive system to cancel the effect of the last command • Often implemented as “Control-Z” • Should support multi-level undo-redo (“Control-Y”), with no limitation other than a possible maximum set by the user

  4. Why are we doing this? • Useful in every interactive application • Don’t even think of writing an interactive system without an undo-redo mechanism • Useful design pattern (“Command”) • Illustration of general algorithmic and data structure techniques • Review of O-O techniques: inheritance, deferred classes, polymorphic data structures, dynamic binding… • Beautiful and elegant

  5. Our working example: a text editor • Notion of “current line”. • Assume commands such as: • Remove current line • Replace current line by specified text • Insert line before current position • Swap current line with next if any • Search and replace: replace every occurrence of a specified string by another • ... • This is a line-oriented view for simplicity, but the discussion applies to more sophisticated views.

  6. Underlying class (from application*) • classEDITABLE_TEXT feature • text: TWO_WAY_LIST [STRING] • remove • -- Delete line at current position. • require • not off • do • text.remove • end • put_right(line: STRING) • -- Insert line after current position. • require • not after • do • text.put_right(line) • end • ... • end *or “model” (cf. MVC)

  7. A straightforward solution • Before performing any operation, save entire stateIn the example: text being edited, • current position in text • If user issues “Undo” request, restore entire state as last saved But: huge waste of resources, space in particular! Intuition: only save the “diff” between states.

  8. Key step in devising a software architecture • Here: • The notion of “command” Finding the right abstractions (the interesting object types)

  9. Keeping the history of the session • The history list: Insertion Insertion Insertion Swap Removal Most recent Oldest history: TWO_WAY_LIST[COMMAND]

  10. What’s a “command” object? • A command object includes information about one execution of a command by the user, sufficient to: • Execute the command • Cancel the command if requested later • For example, in a Removal command object, we need: • The position of the line being removed • The content of that line!

  11. General notion of command feature deferred class COMMAND done: BOOLEAN -- Has this command been executed? execute-- Carry out one execution of this command. requirenot_yet: notdone deferred : doneend ensurealready: done undo -- Cancel an earlier execution of this command. requirealready: done deferred end ensurenot_yet: notdone end

  12. + REMOVAL + INSERTION * COMMAND Command class hierarchy execute* undo* * deferred + effective … execute+undo+ new_line: STRING index: INTEGER ... execute+undo+line: STRINGindex: INTEGER ...

  13. Underlying class (from business model) • classEDITABLE_TEXT feature • text : TWO_WAY_LIST [STRING] • remove • -- Remove line at current position. • require • not off • do • text.remove • end • put_right(line : STRING) • -- Insert line after current position. • require • not after • do • text.put_right (line) • end • ... also item, index, go_ith, put_left ... • end

  14. A command class (1) • classREMOVALinheritCOMMANDfeature • make (t: EDITABLE_TEXT) • -- Remember text, line and position. • do • text := t • index := text.index • line := text.item • end • text: EDITABLE_TEXT • -- Access to business model. • line : STRING • -- Line being removed. • index : INTEGER • -- Position of line being removed. • ...

  15. A command class (2) • ... • execute • -- Remove current line. • do • text.go_i_th (index) • text.remove • done := True • end • undo • -- Re-insert previously removed line. • do • text.go_i_th (index) • text.put_left (line) • done := False • end • end

  16. The history list • A polymorphic data structure: Insertion Insertion Insertion Swap Removal Most recent Oldest history: TWO_WAY_LIST[COMMAND]

  17. Reminder: the list of figures • classLIST [G] • feature • ... • last: Gdo... • extend (x: G)do... • end • fl : LIST [FIGURE] • r : RECTANGLE • s : SQUARE • t : TRIANGLE • p : POLYGON • ... • fl.extend (p); fl.extend (t); fl.extend (s); fl.extend (r)from fl.startuntil fl.afterloopfl.item.display; fl.forthend fl (SQUARE) (TRIANGLE) (RECTANGLE) (POLYGON)

  18. Reminder: a composite figure as a list item forth Cursor

  19. Insertion Insertion Insertion Swap Removal The history list • A polymorphic data structure: Most recent Oldest history: TWO_WAY_LIST[COMMAND]

  20. Insertion Insertion Swap Removal Executing a user command • decode_user_request • if “Request is normal command” then • “Create command object c corresponding to user request” • history.extend(c) • c.execute • elseif “Request is UNDO” then • if not history.beforethen-- Ignore excessive requests • history.item.undo • history.back • end • elseif“Request is REDO” then • if not history.is_lastthen -- Ignore excessive requests • history.forth • history.item.execute • end • end Pseudocode, see implementation next item

  21. A B C D Conditional creation (1) a1: A ifcondition_1then-- “Create a1 as an instance of B” elseifcondition_2then -- “Create a1 as an instance of C” ... etc. … a1: A;b1: B; c1: C; d1: D;... ifcondition_1then create b1.make (...) a1 := b1 elseifcondition_2then create c1.make (...) a1 := c1 ... etc. …

  22. A B C D Conditional creation (2) a1: A ifcondition_1then-- “Create a1 as an instance of B” elseifcondition_2then -- “Create a1 as an instance of C” ... etc. … a1: A ifcondition_1then create{B} a1.make (...) elseifcondition_2then create {C} a1.make (...) ... etc. …

  23. Insertion Insertion Swap Removal Executing a user command • decode_user_request • if “Request is normal command” then • “Create command object c corresponding to user request” • history.extend(c) • c.execute • elseif “Request is UNDO” then • if not history.beforethen-- Ignore excessive requests • history.item.undo • history.back • end • elseif“Request is REDO” then • if not history.is_lastthen -- Ignore excessive requests • history.forth • history.item.execute • end • end item

  24. Creating command objects: first approach • c:COMMAND • text: EDITABLE_TEXT • ... • decode_user_request • if“Request is remove” then create{REMOVAL}c.make(text) • elseif“Request is insert”then • create {INSERTION }c.make(text) • else • etc.

  25. + REMOVAL + INSERTION * COMMAND Command class hierarchy execute* undo* * deferred + effective … execute+undo+ new_line: STRING index: INTEGER ... execute+undo+line: STRINGindex: INTEGER ...

  26. Creating command objects : another approach • Give each command type a number • Initially, fill an array commands with • one instance of every command type. • To get a new command object:“Determine command_type”c := (commands [command_type]).twin commands n command_type ... Swap Insertion 2 Removal 1 Duplicate a “prototype”

  27. The undo-redo (or “command”) pattern • Has been extensively used (e.g. in EiffelStudio and other Eiffel tools) • Fairly easy to implement • Details must be handled carefully (e.g. some commands may not be undoable) • Elegant use of O-O techniques • Disadvantage: explosion of small classes

  28. Using agents • For each user command, have two routines: • The routine to do it • The routine to undo it!

  29. Insertion Insertion Insertion Swap Removal The history list in the undo-redo pattern history: TWO_WAY_LIST[COMMAND] Most recent Oldest

  30. Insertion Insertion Removal Insertion Swap De-insertion De-insertion Re-insertion De-insertion Swap The history list using agents The history list simply becomes a list of agents pairs: history: TWO_WAY_LIST [TUPLE [doer: PROCEDURE [ANY, TUPLE], undoer: PROCEDURE [ANY, TUPLE]] Basic scheme remains the same, but no need for command objects any more; the history list simply contains agents. Named tuple

  31. Insertion Insertion Swap Removal Executing a user command (before) • decode_user_request • if “Request is normal command” then • “Create command object c corresponding to user request” • history.extend(c) • c.execute • elseif “Request is UNDO” then • if not history.beforethen-- Ignore excessive requests • history.item.undo • history.back • end • elseif“Request is REDO” then • if not history.is_lastthen -- Ignore excessive requests • history.forth • history.item.execute • end • end item

  32. Executing a user command (now) • “Decode user_request giving two agentsdo_itand undo_it” • if “Request is normal command” then • history.extend([do_it, undo_it]) • do_it.call([]) • elseif “Request is UNDO” then • if not history.beforethen • history.item.undoer.call([]) • history.back • end • elseif“Request is REDO” then • if not history.is_lastthen • history.forth • history.item.doer.call([])end • end Insertion Insertion Removal Swap De-insertion De-insertion Re-insertion Swap

  33. What we have seen • People make mistakes! • Even when they don’t mess up, they want to experiment: undo-redo supports “trial and error” experimental style • Undo-redo pattern: • Very useful in practice • Widely used • Fairly easy to implement • Excellent illustration of elegant O-O techniques • Even better with agents!

More Related