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:
-
Documentation:
-
Did cosmetic tweaks to existing contents of the User Guide: #14
-
-
Community:
-
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
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 intoHistoryManager
. -
Model#undo()
— Restores the previous coupon stash state fromHistoryManager
. -
Model#redo()
— Restores a previously undone coupon stash state fromHistoryManager
.
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.
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.
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.
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…
.
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:
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.
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.
currStateIndex
is purged.The following activity diagram summarizes what happens when a user executes a new command text:
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.}