1. Overview
This document serves to document my contributions to TopDeck, the project I worked on while taking the introductory software engineering module in NUS. TopDeck is a flash card application built on top of the code from Addressbook Level 4 (AB4).
2. Summary of Contributions
This section summarises my key contributions to TopDeck:
-
Major Enhancement: I added state to the user interface.
-
What it does: A stateful interface looks and behave differently depending on the state of the applicaton.
-
Justification: Having state in the user interface enhances usability in many ways. It offers a useful mental model for users by exposing features only when they are relevant to what a user is currently doing. It allows us to assign multiple related features to the same command word (since they can be disambiguated by state), minimizing the total number of unique commands words. It also leads to shorter commands in general since TopDeck does not have to request information from users that may be already be inferred from the current state.
-
Highlights: AB4 could not be readily adapted to support state because it was designed around assumptions that do not hold with a stateful user interface in full generality. AB4 has a static user interface that assumes there always exists a list of items. This assumption runs so deep that first-class access to
filteredList
andselectedItem
is provided through theModel
API. So I had to rewriteModel
. I also had to modify many subsystems (such as parsing) along the pipeline of a typical command execution. As a result of my work, the rest of my team could work on their features independently and easily integrate their work through the new API I designed.
-
-
Minor Enhancement: I added argument completion to the edit commands (the remaining exposition will focus on the edit card command).
-
What it does: The edit card command has an abbreviated form
edit INDEX
. This prefills the command box with an expanded command which, if submitted verbatim, leaves the card atINDEX
unchanged. -
Justification: This feature is meant to be a shortcut for making small edits. Suppose a user notices a typo in a question. Instead of retyping the entire question, they can quickly expand all arguments to the edit command and edit the question from there.
-
Highlights: Completion is a feature that interacts with the command box and is implemented by defining a new subclass of
CommandResult
that holds the string to be prefilled. In general, commands that need to interact withUI
throughMainWindow
do so by returning an appropriate subclass ofCommandResult
.
-
Code Contributed: CS2103T Dashboard
Other contributions:
-
Project management:
-
I ensured the group stayed on schedule.
-
-
Enhancements to existing features:
-
I refactored a lot of code to support stateful user interface.
-
-
Documentation:
-
I adapted the AB4 developer guide to fit our project.
-
I enhanced the look of our documentation by tweaking the CSS.
-
I polished our documentation according to the standards set by our CS2101 tutor.
-
I proofread sections of the documentation that were not explicitly assigned to anyone.
-
-
Community:
-
Tools:
-
I configured the team repository on Github and integrated the suggested services into our workflow (TravisCI, AppVeyor, Coveralls).
-
3. Contributions to User Guide
This section contains excerpts of my contributions to the user guide. They showcase my ability to write documentation for non-technical users.
3.1. Introduction
TopDeck is a desktop app for anyone who relies on flash cards to memorise things.
TopDeck helps you digitise your flash cards and keeps them organised. You can easily review your cards and TopDeck will track your performance. Improve your memory without the hassle of managing a physical pile of cards!
TopDeck has a Command Line Interface (CLI), which means you interact with it by typing commands. TopDeck can be operated entirely using the keyboard!
If this sounds like the tool you have been looking for, jump to [Quick start] to get started. Enjoy!
3.2. User interface
TopDeck’s interface is very simple. It consists of four main areas - the Menu Bar, the Command Box, the Results Display, and the Content Panel.
Here are the uses of each part of the interface:
-
Menu Bar: Provides access to general commands such as
exit
andhelp
with dropdowns. -
Command Box: This is where you input commands to interact with the application.
-
Results Display: This area shows the outcome of your commands.
-
Content Panel: This is the main display area and changes depending on what you are doing.
TopDeck’s functionality is separated into three distinct views - decks view, cards view and study view. The interface shown in the Content Panel and the commands available change depending on the view. By default, TopDeck starts in decks view.
The figure above provides a brief overview of the different views and shows how you may navigate between them.
In general, the commands open
, study
and back
are used to navigate to cards view, study view and decks view respectively.
For more information about these commands, go to [Commands].
The following sections will describe each view in more detail.
3.3. Decks view
Decks view displays a list of the decks in your collection. The figure to the right shows a typical TopDeck session in decks view.
In decks view, you can:
-
Create a new deck.
-
Edit, delete, or search for an existing deck.
-
Open a deck to view its contents. TopDeck will open the deck in cards view.
-
Pick a deck to study. TopDeck will use the deck in study view.
-
Export a deck in your collection or import a deck from your computer to TopDeck.
You may find the corresponding commands for the features in decks view here.
3.4. Cards view
Cards view displays the cards in a particular deck. The figure to the right shows a typical TopDeck session in cards view.
In cards view, you can:
-
Create a new card and add it to the deck.
-
Edit, delete, or search for an existing card in the deck.
-
View your past study performance for specific cards.
You may find the corresponding commands for the features in cards view here.
3.5. Study view
In study view, TopDeck helps you to study a particular deck. The figure to the right shows a typical TopDeck session in study view.
The cards from a particular deck will be shown one at a time. TopDeck will first show a question. You may then reveal the answer and verify if it matches your answer. Finally, you may rate how well you think you performed for that card before moving on to the next card. TopDeck automates the bookkeeping for you.
You may find a more precise decription of the commands used in cards view here.
4. Contributions to Developer Guide
This section contains excerpts of my contributions to the developer guide. They showcase my ability to write technical documentation and the technical depth of my contributions to TopDeck.
4.1. UI component
The UI
component is responsible for the user interface. It relays user commands to Logic
for execution
and updates the user interface according to the application data in Model
.
UI
consists of a MainWindow
that owns instances of the classes that make up the user interface such as CommandBox
.
Notably, MainWindow
owns a MainPanel
which is a reference type to one of the possible main panels.
Its concrete type is dependent on ViewState
from Model
. The figure below shows how the user interface is divided into classes.
MainWindow
and all its owned classes extend UiPart
which is an abstract class that represents a unit that can be rendered to the display.
UI
uses the JavaFx UI framework. By convention, the layout of each UIPart
is defined in the corresponding .fxml
file in src/main/resources/view
.
For example, the layout of MainWindow
is defined in MainWindow.fxml
.
4.2. Logic component
The Logic
component is responsible for parsing and executing commands. A typical command is parsed and executed as follows:
-
Logic
uses theTopDeckParser
class to parse the user command into a command word and its arguments. -
TopDeckParser
requests for theViewStateParser
from the activeViewState
inModel
. -
This
ViewStateParser
is then used to parse the command word itself. -
This results in a
Command
object which is executed by theLogicManager
. -
The execution of this command may affect the
Model
(e.g. when deleting a deck). -
The result of the command execution is wrapped in a
CommandResult
object and returned back toUi
. -
Different subclasses of
CommandResult
may instructUi
to perform different actions, such asUpdatePanelCommandResult
which is used to construct a newMainPanel
.
To make things clearer, below is the Sequence Diagram for interactions within the Logic
component given an execute("delete 1")
API call.
delete 1
4.3. Stateful user interface
4.3.1. Introduction
TopDeck has a stateful user interface. This means that the set of valid commands and their respective functionality depend on the context of the application state.
For example, the command word add
is "overloaded" with two capabilities:
-
In decks view, it adds a new deck:
add n/DECK_NAME
-
In cards view, it adds a new card to a particular deck:
add q/QUESTION a/ANSWER
It is the active state in TopDeck that resolves the actual command that is called. Also, TopDeck does not request information from the user that is already implicit in the state (e.g. the target deck in the second command).
The reasons for choosing to implement a stateful user interface are manifold. Most importantly, it is necessary to support the implementation of study view which is stateful in nature. A stateful user interface is also preferable to end users since it requires less cognitive effort to operate by virtue of the fewer and shorter commands.
However, implementing state in full generality required nontrivial modifications to the AB4 architecture. These modifications have been reflected in [Architecture]. We will now describe how state is implemented in TopDeck.
4.3.2. Current implementation
States partition the functionalities that are exposed to users. Hence, it is natural to consider distinct views in the user interface as separate states. States in TopDeck correspond to the three possible views described in the user guide: decks view, cards view and study view.
Each state implements a common interface ViewState
and holds transient data that is relevant only while the state is active.
For example, CardsView
has a member activeDeck
which holds a reference to the deck opened in decks view.
Commands in cards view such as add
will then operate on this deck.
The ViewState
contract also requires each implementer to provide policies
for parsing and rendering used by Logic
and Ui
respectively. This is an example of the strategy pattern.
ModelManager
owns the sole instance of ViewState
.
Having only one instance of any state makes it trivial to enforce mutual exclusion.
The Model
is also responsible for executing state transitions.
Each transition is exposed as a method in the Model
API.
For example, Model#changeDeck(Deck deck)
implements the transition from decks view to cards view.
As state entry is handled by the constructors of each state,
the implementation of a transition is as simple as constructing a new state object.
Technically, Model#changeDeck(Deck deck)
can be called from any state, not just decks view.
This is a consequence of the design of Model
.
The Model
API is designed such that no state tracking is necessary.
All methods are expected to work regardless of the current state.
We assume that if a caller is capable of providing the required arguments to a method,
the method call is valid and expected.
This obviates the need for state-checking code in ModelManager
.
4.3.3. Design considerations
The design of the state classes was a significant technical decision. Below were my considerations.
-
Alternative 1: Keep the semantics of the original
Model
and put all state-specific fields and methods here. Maintain an enum to keep track of the active state.-
Pros:
-
Does not require much initial modifications to AB4 to support
-
-
Cons:
-
Model
will contain a lot of irrelevant fields and methods throughout its lifetime such asgetUserAnswer()
. -
Necessary to do a lot of switch-case checking, downcasting to concrete states and error handling of incorrect states.
-
-
-
Alternative 2 (current choice): Redefine the responsibilies of
Model
as above and put state-specific data and methods in the respectiveViewState
classes-
Pros:
-
Separation of concerns. Allows different states to be developed independently.
-
Safety. It is harder to write wrong code if there are no irrelevant fields in a class.
-
Can use polymorphism to dynamically dispatch correct behaviour, obviating the need for switch-case checks.
-
-
Cons:
-
Requires substantial modifications to AB4 to support and requires a rewrite of many tests.
-
Makes testing harder since the model must be initialised to the correct state.
-
-
Despite its initial ease of adoption, alternative 1 is difficult to extend and creates significant technical debt in the long run as more state-specific functionality is added to Model
.
Thus, the choice is clear. Alternative 2 is preferable.