Developer Guide
- Setting up, getting started
- Introduction
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
- Appendix: Instructions for manual testing
Setting up, getting started
Refer to the guide Setting up and getting started.
Introduction
Purpose
This document specifies architecture and software design decisions for the desktop flashcard application, Bagel. It is a living document that evolves throughout the design and implementation for each release.
Scope
This describes the software architecture and software decisions for the implementation of Bagel. The intended audience of this document is the developers, designers, and software testers of Bagel.
Overview
This document focuses on 2 major parts: design and implementation. Under the Design section, you can find details of the system architecture. Under the Implementation section, you can find details of the implementation of some of the commands used in Bagel. In addition to the current document, separate documents and guides on how to use or test Bagel have been included under the Documentation section of this document.
Design
Architecture
The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
Main
has two classes called Main
and MainApp
. It is responsible for,
- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons
represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI
: The UI of the App. -
Logic
: The command executor. -
Model
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
Each of the four components,
- defines its API in an
interface
with the same name as the Component. - exposes its functionality using a concrete
{Component Name}Manager
class (which implements the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component (see the class diagram given below) defines its API in the Logic.java
interface and exposes its functionality using the LogicManager.java
class which implements the Logic
interface.
How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1
.
The sections below give more details of each component.
UI component
API :
Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, FlashcardListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class.
The UI
component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component,
- Executes user commands using the
Logic
component. - Listens for changes to
Model
data so that the UI can be updated with the modified data.
Logic component
API :
Logic.java
-
Logic
uses theBagelParser
class to parse the user command. - This results in a
Command
object which is executed by theLogicManager
. - The command execution can affect the
Model
(e.g. adding a flashcard). - The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
. - In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, such as displaying help to the user.
Given below is the Sequence Diagram for interactions within the Logic
component for the execute("delete 1")
API call.
DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Model component
API : Model.java
The Model
,
- stores a
UserPref
object that represents the user’s preferences. - stores Bagel data.
- exposes an unmodifiable
ObservableList<Flashcard>
that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. - does not depend on any of the other three components.
Storage component
API : Storage.java
The Storage
component,
- can save
UserPref
objects in json format and read it back. - can save Bagel data in json format and read it back.
Common classes
Classes used by multiple components are in the seedu.bagel.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Add Feature
Implementation
The add feature is facilitated by LogicManager and ModelManager. The add command supports the following inputs from the user:
t/TITLE
d/DESCRIPTION
s/SET
l/LINK
tag/TAG
Title and Description are mandatory inputs while the rest are optional inputs. When the user adds a flashcard, the user’s inputs will be passed on to ParserUtil. ParserUtil will conduct input validation and trim any leading and trailing whitespaces. If the user’s inputs are valid and there are no duplicate flashcard, a Flashcard object will be created and added to the UniqueFlashcardList and setOfFlashcardSets in Bagel. Otherwise ParseException will be thrown and the relevant error message will be displayed to the user.
It implements the following operations:
AddCommand#execute() - Add the flashcard to the ModelManager and setOfFlashcardSets AddCommandParser#parse(String args) - Conduct input validation and parse user’s input
The following activity diagram summarizes what happens when a user executes the clear command.
The following sequence diagrams show how the add operation works.
Given below is an example usage scenario and how the add feature behaves at each step.
- The user executes
add t/New Title d/New Desc s/1
to add a flashcard of the titleNew Title
and descriptionNew Desc
into set1
. -
BagelParser
creates anAddCommandParser
and calls its parse method, ParserUtil#parseTitle, ParserUtil#parseDescription, ParserUtil#parseSet, (ParserUtil#parseLink, ParserUtil#parseTags if necessary), with the arguments passed in by the user to check for input validation (no duplicate flashcard in Bagel). - If validation is successful,
AddCommandParser
calls the constructor of Flashcard and creates a flashcard. Consequently, a new AddCommand with the flashcard as the parameter is created. - LogicManager then calls AddCommand#execute(Model model).
- AddCommand then add a flashcard to the flashcard list by calling Model#addFlashcard.
- A CommandResult is generated and Model updates the filteredFlashcardList by adding the flashcard which is then updated in the UI.
- If the set that the newly created flashcard is empty, a button is created in the UI side bar.
Design considerations
-
Current implementation: Flashcard is saved in Bagel upon creation.
- Pros: Easy to implement and CLI-optimized.
- Cons: Could become more complicated in the future as there will be too many prefixes for the
add
command to parse.
Search feature
Implementation
This mechanism makes use of the unmodifiable ObservableList<Flashcard>
in Model
. It filters the given list by searching
for the flashcard that matches the given keyword.
The following sequence diagrams show how the search operation works.
- The user executes
search k/apple
to search flashcards that has matching title, description, or tag to the keyword in the list currently shown. -
BagelParser
creates anSearchCommandParser
and calls its parse method with the arguments passed in by the user. -
SearchCommandParser
returns a newSearchCommand
with thekeyword
to be searched. - When its execute method is called,
SearchCommand
callsupdateFilteredFlashcardList()
with predicate,searchFlashcard
. -
Model
will update flashcards based on the predicate. - The result of this command is returned.
Design consideration
Aspect: How to pass fields to be edited
-
Alternative 1 (current choice): Pass fields to be edited into
SearchCommand
directly- Pros: Easier to implement.
- Cons:
SearchCommand
will have more responsibilities.
-
Alternative 2: Store fields in
SearchFlashcardDescriptor
and pass it intoSearchCommand
- Pros: Better separation of concerns.
- Cons: More code to write.
I chose alternative 1, because only field for search command is keyword
and amount of responsibilities for SearchCommand
will not increase a lot.
View feature
Implementation
This mechanism makes use of the unmodifiable ObservableList<Flashcard>
in Model
. It filters the given list by searching for
for the flashcard that matches the given index.
-
ViewCommand
callsupdateFilteredFlashcardList()
with predicatepredicateViewFlashcard
.
The following sequence diagram shows how the view operation works with parameters.
Design consideration
Aspect: How to pass fields to be edited
-
Alternative 1: Pass fields to be edited into
ViewCommand
directly- Pros: Easier to implement.
- Cons:
ViewCommand
will have more responsibilities.
-
Alternative 2 (current choice): Store fields in
ViewFlashcardParser
and pass it intoViewCommand
- Pros: Better separation of concerns.
- Cons: More code to write.
I chose alternative 2, as the parsing of parameters should be a separate responsibility from the execution of commands.
Sort feature
Implementation
This mechanism makes use of the modifiable ObservableList
in Bagel
itself, as it was not possible to sort the unmodifiable
ObservableList<Flashcard>
in Model
. It directly sorts the ObservableList
in Bagel
with a comparator.
The following sequence diagrams show how the sort operation works.
- The user executes
sort r/tag
to sort flashcards by ascending alphabetical order of their titles. -
BagelParser
creates anSortCommandParser
and calls its parse method with the arguments passed in by the user. -
SortCommandParser
returns a newSortCommand
with theSortRequirement
to be used. -
SortRequirement
returns a newSortByTag
comparator. - When its execute method is called,
SortCommand
callssortFlashcardList()
with the comparator,SortByTag
. - The new sorted
ObservableList<Flashcard>
is set asBagel
’sUniqueFlashcardList
. -
Model
will update the displayed list as the inner list has been modified. - The result of this command is returned.
Design consideration
Aspect: How to pass fields to be edited
-
Alternative 1: Make different
SortCommand
s based on the requirement.- Pros: Easier to implement.
- Cons: More code to write and leads to repeated code.
-
Alternative 2 (current choice): Store fields in
SortCommandParser
andSortRequirement
and pass it intoSortCommand
- Pros: Better separation of concerns.
- Cons: More code to write.
I chose alternative 2, as this would lead to better separation of responsibilities and also leads to reduced repetitive code.
List feature
Implementation
This mechanism makes use of the unmodifiable ObservableList<Flashcard>
in Model
. It filters the list based on the parameters passed with the command word list
.
Its implementation is similar to that of the Search feature, with the difference being in point 4.
- If there are no parameters passed with the command
list
,ListCommand
callsupdateFilteredFlashcardList()
with predicatePREDICATE_SHOW_ALL_FLASHCARDS
. - If a parameter is passed with the command, for example
list s/2
,ListCommand
callsupdateFilteredFlashcardList()
with predicatepredicateShowFlashcardsInSet
.
The following sequence diagram shows how the list operation works with parameters.
Design consideration
Aspect: How to parse the parameters passed with the list
command
-
Alternative 1: Pass it into the ListCommand class directly.
- Pros: Easier to implement.
- Cons:
ListCommand
has more responsibilities, such as to parse the presence of parameters.
-
Alternative 2 (current choice): Create a
ListCommandParser
to parse parameters if there are any.- Pros: Delegates the different responsibilities to each class.
- Cons: More code to write, as the original implementation of the
list
command in AB3 did not allow parameters to be passed.
I chose alternative 2, because even though the increase in responsibility for ListCommand
is rather minimal, parsing of parameters should still be separated from execution of commands.
Edit Feature
Implementation
The edit mechanism involves an additional EditFlashcardDescriptor
class to pass the content of the fields to be edited into EditCommand
.
The following sequence diagrams show how the edit operation works.
- The user executes
edit 1 t/New Title
to edit the title of the first flashcard in the list currently shown. -
BagelParser
creates anEditCommandParser
and calls its parse method with the arguments passed in by the user. -
EditCommandParser
creates anEditFlashcardDescriptor
. - For each field in the flashcard,
EditCommandParser
checks if there is an updated version provided by the user. If there is, the new content is added into theEditFlashcardDescriptor
. -
EditCommandParser
returns a newEditCommand
with the index of theFlashcard
to be edited and theEditFlashcardDescriptor
. - When its execute method is called,
EditCommand
gets theFlashcard
to edit fromModel
. -
EditCommand
creates a newFlashcard
based on theEditFlashcardDescriptor
and theFlashcard
to be edited. - This new
Flashcard
replaces the oldFlashcard
inModel
. - The result of this command is returned.
Design consideration
Aspect: How to pass fields to be edited
-
Alternative 1 (current choice): Store fields in
EditFlashcardDescriptor
and pass it intoEditCommand
- Pros: Better separation of concerns.
- Cons: More code to write.
-
Alternative 2: Pass fields to be edited into
EditCommand
directly- Pros: Easier to implement.
- Cons:
EditCommand
will have more responsibilities.
Clear feature
Implementation
The following activity diagram summarizes what happens when a user executes the clear command.
The Clear mechanism will allow the user to delete all flashcards in their local hard drive (local Bagel).
The implementation makes use of the Model#setBagel
and Bagel
. A new instance of Bagel
without any existing flashcards will replace the users current Bagel
in Model
.
Usage
Given below is an example usage scenario and how the Clear feature behaves at each step.
Step 1. The user executes clear
command to clear all flashcards in his local QuickCache.
Step 2. ClearCommand#execute
will replace the current instance of QuickCache
with a new empty instance of QuickCache
through the Model#setQuickCache
method.
Step 3. After execution, CommandResult
will contain a message indicating that it has cleared QuickCache.
- The user executes
clear
to clear all flashcards in their local Bagel. -
ClearCommand#execute
will replace the current instance ofBagel
with a new empty instance ofBagel
through theModel#setBagel
method. - After execution, a
CommandResult
is generated and will contain a message indicating that it has successfully cleared Bagel.
The following sequence diagram shows how the Clear mechanism works:
Design Considerations:
Aspect: How to clear all the current flashcards in Bagel
-
Alternative 1 (current choice): Replaces the existing
Bagel
in model with a newBagel
that is empty.- Pros: Easy to implement - minimizes the occurrence of bugs or remnant flashcards that failed deletion.
- Cons: Waste of resources/inefficient as a new
Bagel
instance needs to be created when a user wants to clear Bagel.
-
Alternative 2: Delete all the flashcards currently in Bagel by reiteration
- Pros: More efficient than alternative 1 if there are only a few flashcards in Bagel at the moment of clearance.
- Cons: Rate of inefficiency increases exponentially with the number of flashcards added into Bagel.
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- computing students taking GER1000
- wants to memorise content taught in GER1000
- has a need to manage a significant number of flashcards
- prefer desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value proposition: Bagel is a flashcard application faster than a typical mouse/GUI driven app with additional functionality such as adding tags, links, or comments. It targets computing students taking GER1000 because it is a module that requires memorising many keywords and items, and computing students would prefer typing over other means of input.
User stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * |
user | add flashcards | memorise them |
* * * |
user | delete my flashcards when I am done with them | keep the list organised |
* * * |
user | edit a flashcard | rephrase or fix mistakes that I originally wrote |
* * * |
user | view individual flashcards | read them |
* * * |
user | view a list of sets of flashcards that I currently have | not mix them up |
* * * |
user | “flip” through a set of flashcards | memorise them |
* * |
user | tag flashcards | revise a certain topic easily |
* * |
forgetful user | search for flashcards | |
* * |
user | sort my flashcards | keep the list organised |
* * |
user | add links to my flashcards | find the particular lecture slide/notes by clicking on it |
* * |
user | add flashcards to sets | memorise relevant flashcards together |
* |
first time user | view all possible commands | navigate the app easily |
* |
user ready to start using the app | clear all flashcards | get rid of sample/experimental flashcards I used for exploring the app |
Use cases
(For all use cases below, the System is Bagel
and the Actor is the user
, unless specified otherwise)
Use case: UC01 - Adding a flashcard
MSS
- User chooses to add a flashcard.
- User enters the relevant details of the flashcard they would like to add.
- System adds the flashcard and shows the new list of flashcards.
Use case ends.
Extensions
- 2a. System detects an error in the entered data.
- 2a1. System requests for the correct data.
- 2a2. User enters new details.
Steps 2a1-2a2 are repeated until the data entered are correct.
Use case resumes from step 3.
Use case: UC02 - Deleting a flashcard
MSS
- User chooses to delete a flashcard.
- User lists the entire list of flashcards. (UC)
- User enters the index of the flashcard they would like to delete.
- System deletes the flashcard and shows the new list of flashcards.
Use case ends.
Extensions
- 3a. Similar to extension of UC01.
Use case resumes from step 4.
Use case: UC03 - Editing a flashcard
MSS
- User chooses to edit a flashcard.
- User lists the entire list of flashcards. (UC05)
- User enters the index of the flashcard they would like to edit, and the details to edit.
- System edits the flashcard and shows the edited flashcard.
Use case ends.
Extensions
- 3a. Similar to extension of UC01.
Use case resumes from step 4.
Use case: UC04 - Viewing a flashcard
MSS
- User chooses to view a flashcard.
- User enters the index of the flashcard they would like to view.
- System displays the flashcard.
Use case ends.
Extensions
- 2a. Similar to extension of UC01.
Use case resumes from step 3.
Use case: UC05 - Listing all flashcards
MSS
Similar to UC04, except user enters relevant details for listing all flashcards.
Use case: UC06 - Flipping through flashcards
MSS
- User chooses to flip through the list of flashcards.
- User enters the relevant details to start viewing from the first flashcard in the list.
- System shows the first flashcard.
- User enters the relevant details to view the next flashcard in the list.
- System shows the next flashcard.
Steps 4-5 are repeated for each flashcard, until the user reaches the end of the list. - System shows the first flashcard.
Use case ends.
Extensions
- 2a/4a. Similar to extension of UC01.
Use case resumes from step 3/5.
Use case: UC07 - Searching through flashcards
MSS
Similar to UC04, except user enters relevant details for searching.
Use case resumes from step 3.
Use case: UC08 - Sorting flashcards
MSS
Similar to UC04, except user enters relevant details for sort.
Use case: UC09 - Clearing flashcards
MSS
Similar to UC04, except user enters relevant details for clear.
Non-Functional Requirements
- Bagel should work on any mainstream OS as long as it has Java
11
or above installed. - Bagel should be able to hold up to 500 flashcards without a noticeable sluggishness in performance for typical usage.
- Bagel should be intuitive to use for first time users.
- Bagel should be targeting users who can type fast and prefer typing over other means of input.
- Bagel should respond within two seconds.
- Bagel should show flashcards within one second of inputting the command.
- Any version of Bagel should function as long as the user has it installed.
- Bagel is expected to adhere to the CS2103T schedule.
- Bagel is not expected to quiz the user.
- Bagel should be for a single user.
Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
- Command Line Interface (CLI): Text based user interface.
- Graphical User Interface (GUI): User interface that allows users to interact via icons and graphics.
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.
Launch and shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder.
-
Double-click the jar file.
Expected: Shows the GUI with a set of sample flashcards. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
Deleting a flashcard
-
Deleting a flashcard while all flashcards are being shown
-
Prerequisites: List all flashcards using the
list
command. Multiple flashcards in the list. -
Test case:
delete 1
Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. -
Test case:
delete 0
Expected: No flashcard is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete
,delete x
,...
(where x is larger than the list size)
Expected: Similar to previous.
-
Saving data
-
Dealing with missing/corrupted data files
-
Open the
bagel.json
file indata
folder. If it does not exist, run the jar file and typeexit
. -
Edit the
bagel.json
file so that it contains invalid information. For example, add a space in a flashcard’s tags. -
Re-launch the app.
Expected: Bagel will start with no flashcards.
-