Skip to content

Commit

Permalink
[se-edu#582] Developer Guide: Explain undo and redo mechanism (se-edu…
Browse files Browse the repository at this point in the history
…#587)

CS2103 students will need to explain their design decisions made in
their project. The write up needs to be done inside the Developer Guide.

There are no such examples in the Developer Guide for the students to
follow.

Let's add a section to explain undo and redo, to show the students how
it should be done.
  • Loading branch information
yamgent authored Jul 30, 2017
1 parent f5ca8d6 commit c27ad03
Show file tree
Hide file tree
Showing 15 changed files with 127 additions and 0 deletions.
127 changes: 127 additions & 0 deletions docs/DeveloperGuide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,133 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa

== Implementation

// tag::undo[]
=== Undo/Redo mechanism

The undo/redo mechanism is facilitated by an `UndoRedoStack`, which resides inside `LogicManager`. It supports undoing and redoing of commands that modifies the state of the address book (e.g. `add`, `edit`). Such commands will inherit from `UndoableCommand`.

`UndoRedoStack` only deals with `UndoableCommands`. Commands that cannot be undone will inherit from `Command` instead. The following diagram shows the inheritance diagram for commands:

image::LogicCommandClassDiagram.png[width="800"]

As you can see from the diagram, `UndoableCommand` adds an extra layer between the abstract `Command` class and concrete commands that can be undone, such as the `DeleteCommand`. Note that extra tasks need to be done when executing a command in an _undoable_ way, such as saving the state of the address book before execution. `UndoableCommand` contains the high-level algorithm for those extra tasks while the child classes implements the details of how to execute the specific command. Note that this technique of putting the high-level algorithm in the parent class and lower-level steps of the algorithm in child classes is also known as the https://www.tutorialspoint.com/design_pattern/template_pattern.htm[template pattern].

Commands that are not undoable are implemented this way:
[source,java]
----
public class ListCommand extends Command {
@Override
public CommandResult execute() {
// ... list logic ...
}
}
----

With the extra layer, the commands that are undoable are implemented this way:
[source,java]
----
public abstract class UndoableCommand extends Command {
@Override
public CommandResult execute() {
// ... undo logic ...
executeUndoableCommand();
}
}
public class DeleteCommand extends UndoableCommand {
@Override
public CommandResult executeUndoableCommand() {
// ... delete logic ...
}
}
----

Suppose that the user has just launched the application. The `UndoRedoStack` will be empty at the beginning.

The user executes a new `UndoableCommand`, `delete 5`, to delete the 5th person in the address book. The current state of the address book is saved before the `delete 5` command executes. The `delete 5` command will then be pushed onto the `undoStack` (the current state is saved together with the command).

image::UndoRedoStartingStackDiagram.png[width="800"]

As the user continues to use the program, more commands are added into the `undoStack`. For example, the user may execute `add n/David ...` to add a new person.

image::UndoRedoNewCommand1StackDiagram.png[width="800"]

[NOTE]
If a command fails its execution, it will not be pushed to the `UndoRedoStack` at all.

The user now decides that adding the person was a mistake, and decides to undo that action using `undo`.

We will pop the most recent command out of the `undoStack` and push it back to the `redoStack`. We will restore the address book to the state before the `add` command executed.

image::UndoRedoExecuteUndoStackDiagram.png[width="800"]

[NOTE]
If the `undoStack` is empty, then there are no other commands left to be undone, and an `Exception` will be thrown when popping the `undoStack`.

The following sequence diagram shows how the undo operation works:

image::UndoRedoSequenceDiagram.png[width="800"]

The redo does the exact opposite (pops from `redoStack`, push to `undoStack`, and restores the address book to the state after the command is executed).

[NOTE]
If the `redoStack` is empty, then there are no other commands left to be redone, and an `Exception` will be thrown when popping the `redoStack`.

The user now decides to execute a new command, `clear`. As before, `clear` will be pushed into the `undoStack`. This time the `redoStack` is no longer empty. It will be purged as it no longer make sense to redo the `add n/David` command (this is the behavior that most modern desktop applications follow).

image::UndoRedoNewCommand2StackDiagram.png[width="800"]

Commands that are not undoable are not added into the `undoStack`. For example, `list`, which inherits from `Command` rather than `UndoableCommand`, will not be added after execution:

image::UndoRedoNewCommand3StackDiagram.png[width="800"]

The following activity diagram summarize what happens inside the `UndoRedoStack` when a user executes a new command:

image::UndoRedoActivityDiagram.png[width="200"]
// end::undo[]

==== Design Considerations

**Aspect:** Implementation of `UndoableCommand` +
**Alternative 1 (current choice):** Add a new abstract method `executeUndoableCommand()` +
**Pros:** We will not lose any undone/redone functionality as it is now part of the default behaviour. Classes that deal with `Command` do not have to know that `executeUndoableCommand()` exist. +
**Cons:** Hard for new developers to understand the template pattern. +
**Alternative 2:** Just override `execute()` +
**Pros:** Does not involve the template pattern, easier for new developers to understand. +
**Cons:** Classes that inherit from `UndoableCommand` must remember to call `super.execute()`, or lose the ability to undo/redo.

---

**Aspect:** How undo & redo executes +
**Alternative 1 (current choice):** Saves the entire address book. +
**Pros:** Easy to implement. +
**Cons:** May have performance issues in terms of memory usage. +
**Alternative 2:** Individual command knows how to undo/redo by itself. +
**Pros:** Will use less memory (e.g. for `delete`, just save the person being deleted). +
**Cons:** We must ensure that the implementation of each individual command are correct.

---

**Aspect:** Type of commands that can be undone/redone +
**Alternative 1 (current choice):** Only include commands that modifies the address book (`add`, `clear`, `edit`). +
**Pros:** We only revert changes that are hard to change back (the view can easily be re-modified as no data are lost). +
**Cons:** User might think that undo also applies when the list is modified (undoing filtering for example), only to realize that it does not do that, after executing `undo`. +
**Alternative 2:** Include all commands. +
**Pros:** Might be more intuitive for the user. +
**Cons:** User have no way of skipping such commands if he or she just want to reset the state of the address book and not the view. +
**Additional Info:** See our discussion https://github.com/se-edu/addressbook-level4/issues/390#issuecomment-298936672[here].

---

**Aspect:** Data structure to support the undo/redo commands +
**Alternative 1 (current choice):** Use separate stack for undo and redo +
**Pros:** Easy to understand for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project. +
**Cons:** Logic is duplicated twice. For example, when a new command is executed, we must remember to update both `HistoryManager` and `UndoRedoStack`. +
**Alternative 2:** Use `HistoryManager` for undo/redo +
**Pros:** We do not need to maintain a separate stack, and just reuse what is already in the codebase. +
**Cons:** Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as `HistoryManager` now needs to do two different things. +

=== Logging

We are using `java.util.logging` package for logging. The `LogsCenter` class is used to manage the logging levels and logging destinations.
Expand Down
Binary file added docs/diagrams/UndoRedoActivityDiagram.pptx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added docs/diagrams/UndoRedoSequenceDiagram.pptx
Binary file not shown.
Binary file added docs/diagrams/UndoRedoStartingStackDiagram.pptx
Binary file not shown.
Binary file added docs/images/UndoRedoActivityDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/UndoRedoExecuteUndoStackDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/UndoRedoNewCommand1StackDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/UndoRedoNewCommand2StackDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/UndoRedoNewCommand3StackDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/UndoRedoSequenceDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/UndoRedoStartingStackDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c27ad03

Please sign in to comment.