PROJECT: AddressBook - Level 3


Overview

AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.

Summary of contributions

  • Major enhancement: added the ability to undo/redo previous commands

    • What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.

    • Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.

    • Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.

    • Credits: {mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}

  • Minor enhancement: added a history command that allows the user to navigate to previous commands using up/down keys.

  • Code contributed: [Functional code] [Test code] {give links to collated code files}

  • Other contributions:

    • Project management:

      • Managed releases v1.3 - v1.5rc (3 releases) on GitHub

    • Enhancements to existing features:

      • Updated the GUI color scheme (Pull requests #33, #34)

      • Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests #36, #38)

    • Documentation:

      • Did cosmetic tweaks to existing contents of the User Guide: #14

    • Community:

      • PRs reviewed (with non-trivial review comments): #12, #32, #19, #42

      • Contributed to forum discussions (examples: 1, 2, 3, 4)

      • Reported bugs and suggestions for other teams in the class (examples: 1, 2, 3)

      • Some parts of the history feature I added was adopted by several other class mates (1, 2)

    • Tools:

      • Integrated a third party library (Natty) to the project (#42)

      • Integrated a new Github plugin (CircleCI) to the team repo

{you can add/remove categories in the list above}

Contributions to the User Guide

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

Deleting a coupon: delete

Deletes the specified coupon from the Coupon Stash.

Format: delete INDEX

  • Deletes the coupon at the specified INDEX.

Examples:

  • list
    delete 2
    Deletes the second coupon in the coupon list.

  • find rice
    delete 1
    Deletes the first coupon in the resulting coupon list of the find command.

Contributions to the Developer Guide

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

Undo/Redo feature

The undo/redo mechanism is facilitated by with an undo/redo history, stored internally as an couponStashStateList with a commandTextHistory and currStateIndex. All these components are encapsulated in the HistoryManager class. The following methods in the Model interface facilitates this feature:

  • Model#commitCouponStash(String commandText) — Saves the current coupon stash state and the command text that triggered the change in state into HistoryManager.

  • Model#undo() — Restores the previous coupon stash state from HistoryManager.

  • Model#redo() — Restores a previously undone coupon stash state from HistoryManager.

Current Implementation

Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.

Step 1. The user launches the application for the first time. The CouponStash will be initialized with the initial coupon stash state, and the currStateIndex pointing to that single coupon stash state.

UndoRedoState0
Figure 1. CouponStash will be initialized with the initial coupon stash state.

Step 2. The user executes delete 5 command to delete the 5th coupon in the coupon stash. The delete command calls Model#commitCouponStash(String commandText), causing the modified state of the coupon stash after the delete 5 command executes to be saved in the couponStashStateList, and the delete 5 command text to be stored in the commandTextHistory. currStateIndex is shifted to the newly inserted coupon stash state.

UndoRedoState1
Figure 2. currStateIndex is shifted to the newly inserted coupon stash state.

Step 3. The user executes add n/OMO STORE …​ to add a new coupon. The add command also calls Model#commitCouponStash(String commandText), causing another modified coupon stash state and command text to be saved into the couponStashStateList and commandTextHistory respectively.

UndoRedoState2
Figure 3. Modified coupon stash state and command text are saved into the couponStashStateList and commandTextHistory respectively.
If a command fails its execution, it will not call Model#commitCouponStash(String commandText), so the coupon stash state and command text will not be saved.

Step 4. The user now decides that adding the coupon was a mistake, and decides to undo that action by executing the undo command. The undo command will call Model#undoCouponStash(), which will shift the currStateIndex once to the left, pointing it to the previous coupon stash state, and restores the coupon stash to that state. Plus, the command text is returned, thus allowing for the display of the command that was undone. In this case, the command undone is add n/OMO STORE…​.

UndoRedoState3
Figure 4. currStateIndex shifted once to the left.
If the currStateIndex is at index 0, pointing to the initial coupon stash state, then there are no previous coupon stash states to restore. The undo command uses Model#canUndoCouponStash() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.

The following sequence diagram shows how the undo operation works:

UndoSequenceDiagram
Figure 5. Undo operation sequence diagram.

The redo command does the opposite — it calls Model#redoCouponStash(), which shifts the currStateIndex once to the right, pointing to the previously undone state and command text, and restores the coupon stash to that state. Finally, it returns the redone command text.

If the currStateIndex is at index couponStashStateList.size() - 1, pointing to the latest coupon stash state, then there are no undone coupon stash states to restore. The redo command uses Model#canRedoCouponStash() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.

Step 5. The user then decides to execute the command list. Commands that do not modify the coupon stash, such as list, will not call Model#commitCouponStash(). Thus, the couponStashStateList remains unchanged.

UndoRedoState4
Figure 6. couponStashStateList remains unchanged.

Step 6. The user executes clear, which calls Model#commitCouponStash(). Since the currStateIndex is not pointing at the end of the couponStashStateList, all coupon stash states and command text history after the currStateIndex will be purged. We designed it this way because it no longer makes sense to redo the add n/OMO STORE …​ command. This is the behavior that most modern desktop applications follow.

UndoRedoState5
Figure 7. Command text history after the currStateIndex is purged.

The following activity diagram summarizes what happens when a user executes a new command text:

CommitActivityDiagram
Figure 8. Activity diagram representation of how command texts are saved when they are executed.

Design Considerations

Aspect: How undo & redo executes
  • Alternative 1 (current choice): Saves the entire coupon stash.

    • Pros: Easy to implement.

    • Cons: May have performance issues in terms of memory usage. Plus, have to perform deep copy of coupons when saving the coupon stash so as to prevent unwanted mutations.

  • Alternative 2: Individual command knows how to undo/redo by itself.

    • Pros: Will use less memory (e.g. for delete, just save the coupon being deleted).

    • Cons: We must ensure that the implementation of each individual command is correct.

Alternative 1 was chosen due to its relative simplicity and extensibility. Little to no modification needs to be made to each command that can be undone, thus reducing chances of new bugs surfacing. Additionally, the ability to undo operations such as clear will require alternative 2 to copy the entire coupon stash too, so both alternatives will have the same memory footprint in such a context. Finally, the real world performance impact of copying all coupons vs copying one is not very huge. Thus, the more extensible and simpler alternative 1 was chosen.

PROJECT: PowerPointLabs


{Optionally, you may include other projects in your portfolio.}