subbashcontentpage1

CS2103T Team Project: Dukemon

subbashcontentpageA1

Dukemon is a desktop flashcard application used to reinforce memory techniques. The user interacts with it using a CLI (Command Line Interface), and it has a GUI (Graphical User Interface) created with JavaFX.

While it was targeted at regular CLI users, after preliminary user experience feedback from the target audience, we noted that even experienced users require documentation to get familiar with commands. Also, they found it inconvenient to refer to the User Guide often.

It was our priority to improve their learning experience. So I took the initiative to go the extra mile to improve their command line experience.

Under the hood, it required major restructuring of input handling. In that process, I took care of that code was well abstracted for co-developers and future developers.

subbashcontentpageB1

AutoComplete with Dynamic Command Parsers

  • Benefits Users

    • What it does: allows users to be visually cued on the commands available that updates as he types.

    • Justification: It alleviates the drawback of being clueless and disoriented in a new command line interface

    • Highlights: The parser logic for autocomplete was designed from the ground up to cater for the application’s unique needs. After in-depth analysis of alternatives, the advanced paradigm of java reflections was utilised.

In-depth explanation in User Guide extract below
  • Benefits Developers

    • What it does: Allows new commands and parsers to be added with just one line of code

    • Justification: Abides open close principle of object oriented programming paradigm.

    • Highlights: Internal architecture was made succinct without unnecessary complications.

In-depth analysis in Developer Guide extract below

Project management

  • Coordinated development branch and master branch workflow for rapid prototyping in early weeks.

  • Suggested branching workflow (with username, merge count and purpose) during period closer to deployment for stability and accountability in changes.

  • Initial setup of continuous integration pipeline with Travis.

  • Managed product release on GitHub v1.3.5

  • Actively participated in weekly team meetings that focused on restructuring of code base and to get everyone on the same page.

Community

PR reviewed: CS2103T-T10-4 TimeBook

subbashcontentpageC1

Below are extracts I have contributed to the User Guide.

It demonstrates how my AutoComplete feature makes a tangible difference in improving user flow.

Also its a showcase of my ability to write documentation targeting end-users.

Switching Modes

There are 4 application modes.

UGModes
Figure 1. Application modes
RestrictedHomeMode
Figure 2. Mode Display

In the highlighted section above, you can see the current mode you are in and the available modes.

To transition between them you have to enter the SwitchCommand that represents each mode into the Command Box that says Enter command here...

  • open Enter
    to enter open mode

  • start Enter
    to enter game mode

  • settings Enter
    to enter settings mode

  • home Enter
    to enter home mode

Requirements before changing mode

  • A bank should be selected

  • No game should be running

ModesAvailable
Figure 3. If other modes are available, they will be displayed beside the Command Box

Yes, it feels like a steep learning curve >_<

But do not worry as we have the AutoComplete Bar that auto completes the available commands whichever mode you are in.

AutoComplete Bar

AutoCompleteBar
Figure 4. AutoComplete Bar

The highlighted section shows, what commands are currently available. You can click them to automatically fill it in for you. Each of your keystroke will dynamically update the AutoComplete bar, just like the keyboard on your smartphone.

QuickStart

Let’s select the sample WordBank and play a game to get familiar.

  1. select sample Enter

    • This would allow you to switch modes

  2. open Enter

  3. start Enter

  4. guess <your_guess> Enter

    • keep guessing till the statistics screen appears

    • you can switch modes now

  5. home Enter

Getting comfortable? Ready to master the application commands?

Some typical commands to get familiar with are:

  • create <NAME>: Creates an empty WordBank with specified name. (in Home Mode)

  • select <NAME>: Selects and switch to WordBank with the specified name. (in Home Mode)

  • add w/<WORD> m/<MEANING>: Adds a new Card with specified Word and Meaning into the current WordBank. (in Open Mode)

  • list: Lists all Cards in the current WordBank. (in Open Mode)

  • start <EASY/MEDIUM/HARD>: Starts a Game session with the specified Difficulty. Default difficulty in Settings will be used if not specified. (after selecting WordBank)

  • guess <YOUR_ANSWER>: Makes a Guess for the current Word whose Meaning is shown on the UI. (in Game Mode)

  • stop: Stops the current Game session. (in Game Mode)

  • exit: Exits Dukemon. (in any mode except Game)

Purposes of each mode

HomeMode
  • Create/Choose a Wordbank

  • View Global Statistics

OpenMode
  • Create/Add/Modify Cards of your WordBank. (Each Card contains a Word and Meaning).

  • View Statistics belonging to a specific WordBank

GameMode
  • Guess Words based on each Meaning that appear as quickly as possible.

  • Finish the Game and view the Statistics for your game session.

SettingsMode
  • Configure your preferred Settings. (change Difficulty, Theme etc.)

subbashcontentpageD1

Below are extracts I have contributed to the Developer Guide.

It demonstrates how my AutoComplete feature works with the Dynamic Parser implementation.

Also its a showcase of my ability to write technical documentation and the technical depth of my work that involves integrating the advanced paradigm of Java Reflections into the project.

Logic component

This section breakdown the logic package into its internal components

LogicClassDiagram
Figure 5. Structure of the Logic Component

Logic is primarily built by two segments: Command and Parser.

Command
Command is an abstract class.
Four other abstract classes (WordBankCommand, CardCommand, GameCommand and SettingsCommand) extend Command.
Concrete Command classes with an execute method implementation extend one of the above four abstract classes.
Parser
ParserManager holds reference to two SpecificModeParsers
The SpecificModeParsers change based on current application mode.
They hold references to all concrete Parser and Command Classes with the help of ClassUtil

Logic fulfils its contracts with other packages through two interfaces: Logic and UiLogicHelper

Interaction through Logic Interface

Examples of transactions promised by Logic API include command execution, command result and update statistics.

  • Command Execution through Logic Interface

    1. A String from Ui package gets to ParserManager and gets converted into a Command object which is executed by the LogicManager.

    2. The command execution can affect the Model (e.g. adding a word meaning pair into wordbank).

    3. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui and AppManager.

    4. In addition, the CommandResult object can also instruct the Ui to perform certain actions, such as displaying help to the user.

Interaction through UiLogicHelper Interface

UiLogicHelper APIs is a subset of Logic APIs and only contains transactions for AutoComplete. It exposes the functionalities through the following getter methods:

  • List<AutoFillAction>#getMenuItems(String text) — Gets a List of AutoFillActions to fill up AutoComplete display based on current user input given in text

  • ModeEnum#getMode() — Retrieves the application mode to display visually to the user (represented by enumeration object ModeEnum)

  • List<ModeEnum>#getModes() — Retrieves the possible modes the user can transition to from current mode

The following sequence diagram shows how the AutoComplete operation runs when user keys in "st" into command box.

AutoCompleteSequenceDiagram
Figure 6. Sequence Diagram of AutoComplete

AutoComplete/Parser Feature

This section explains how the design choice of Dynamic Parsers fulfils AutoComplete and Command Execution.

ParserManager dynamically changes parser depending on current mode the game is at. This is modeled using the Strategy Pattern. https://en.wikipedia.org/wiki/Strategy_pattern.

Instead of choosing a single parser to use at compile time, they are chosen at runtime depending on runtime state. This supports a variety of benefits which are explained under design considerations.

The above implementation empowers the application with the following features :

  1. Every user keystroke only auto completes the right commands

  2. Only the right commands get parsed and executed. What are the right commands? They are the commands that belong to the current mode and switch commands when preconditions are met.

Implementation details of ParserManager

  1. ParserManager instance has reference to two SpecificModeParser objects

  2. When user enters a keystroke, the SpecificModeParser which holds switch commands or SpecificModeParser which holds current mode commands are accessed based on internal state.

  3. Internal State consists of booleans: gameIsOver, bankLoaded and enumeration ModeEnum: HOME, OPEN, GAME, SETTINGS

  4. Boolean algebra is used to derive the four overall states.

    The below activity diagram demonstrates four possible states and a typical user flow.
StateActivityDiagram1
Figure 7. Activity diagram of a typical application flow
  • Definitions of Switch and Mode in table above

    • SwitchCommands = (commands that change mode)

    • ModeCommands = (commands that belong to a specific mode ie Home, Open, Game and Settings)

Implementation details of SpecificModeParser

  • SpecificModeParsers use ClassUtil to handle instantiation of Parser and Command objects.

  • ClassUtil holds a list of references to Command and Parsers classes. In Java class references are passed using .class attribute. Example: AddCommand.class

  • Internally, ClassUtil employs java reflections to find attributes of classes without instantiating them. Code for it is succinct and shown in the snippet linked here.

  • Also, when a command needs to be executed, it instantiates the Parser object (if any) and Command object at runtime.

  • Here is a snippet is from ParserManager. Just one line of code is necessary to include a new command with its parser. Example:

    temp.add(NewCommand.class, NewCommandParser.class);

Design Considerations

Alternative 1

Alternative 2

Aspect 1:
How parser and command objects are instantiated in SpecificModeParser

Use java reflections to hold a list of classes and iterate through them to pick the matching classes

Pros:
Open Close Principle strictly followed. Adding a command and a parser takes only one line of code.

Cons:
It is developer’s responsibility to ensure classes subclass the abstract Command class as compile time errors would not be thrown.

Use switches to match the command word with the right parsers

Pros:
Compile time error would be thrown if new command or parser does not subclass correctly.

Cons:
Adding a new command with parser would require the developer to insert it into multiple locations as the autocomplete feature needs an iterable command list.

Why did we choose Alternative 1:
Given that ClassUtil gracefully handles wrongly passed class references, the lack of compile time check does not impair the functionality of the application. Furthermore, alternative 1 prevents code duplication for autocomplete and executing.

Aspect 2:
Single Parser vs Parser Manager

Using a ParserManager to dynamically switch between Parsers based on current state

Pros:
Commands not belonging to specific mode would not be parsed

Cons:
More code to write for initial developer.

Use a single parser

Pros
We do not need to restructure the logic package.

Cons
Bad user experience as it autocompletes and parses commands that do not belong to a particular mode.

Why did we choose Alternative 1:
As commands are stateful, it would be easy to overlook the edge cases when so many combinations and permutations are likely. Segregating them by modes allows a better user experience and minimises the possibilities of bugs. Also, future extensibility is improved for new modes and parsers as the Open Close Principle is abided.