PROJECT: Calgo

Overview

This portfolio page highlights some of my contributions to Calgo - a Software Engineering project developed in my second year of undergraduate studies in the National University of Singapore.

About the Team

We are 5 Year 2 Computer Science undergraduates reading CS2103T: Software Engineering.

About the Project

Calgo is an all-in-one personal meal tracking assistant which seeks to encourage a healthy lifestyle among its users. It allows users to not only have a convenient nutritional record of all their favourite food entries, but also track, monitor, and plan their food consumption. Moreover, the team has come up with a plethora of user-centric features to make Calgo well-suited to provide users with both convenience and utility.

My team was tasked with morphing an existing Address Book Level 3 (AB3) project into a new product via Brownfield software development. We were therefore required to use the existing AB3 project as Calgo’s project foundation, to create a desktop application supporting the Command Line Interface. This was to target users who prefer typing but also enjoy the benefits of a Graphical User Interface. With all of us being food lovers and realising a greater societal need for healthy eating, Calgo was born.

Summary of contributions

Enhancements

  • Major enhancement: I implemented categorical search via the find command.

    • What it does: This allows users to search for Food entries by narrowing down on a specific Food-related attribute of either Name, Calorie, Protein, Carbohydrate, Fat, or Tag.

    • Justification: Users now have an option to perform refined searches, preventing the need to tediously scroll through the Food Record.

    • Highlights: This enhancement requires in-depth understanding of the application’s architecture. A new parser for the find command was created to detect different prefixes entered by the user, as with supporting classes to facilitate background workings.

  • Major enhancement: I implemented the export command.

    • What it does: This allows users to export their current Food entries to a portable, neatly formatted editable .txt file. They can now also add their own notes and share their entries.

    • Justification: This improves user experience as they can now obtain a copy of their Food entries for multiple purposes like printing onto paper.

    • Highlights: This enhancement requires comprehensive understanding of how commands are processed. A new class for the command, as well as supporting classes (such as for table formatting) were created. Moreover, as the report command is similar, we applied good OOP practices for better code quality and reuse.

  • Minor enhancement: I implemented substring search via the find command.

    • What it does: This allows users to search for Food entries by typing incomplete keywords for searching using the Name or Tag prefixes. Results are entries containing these substrings.

    • Justification: Users no longer have to type the full keyword. Those who are lazy, or happen to enter incomplete keywords can still have their results shown.

    • Highlights: This enhancement relies on Predicate s, as with categorical search above.

  • Minor enhancement: I implemented the lexicographical ordering of Food objects.

    • What it does: This makes the Food Record show all entries in lexicographical order.

    • Justification: It is frustrating to scroll through messy entries.

    • Highlights: We perform sorting only when Calgo starts up or when new entries are added for efficiency.

  • Code contributed: You can view my contributions to Calgo here.

  • Other contributions:

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. Please note that some hyperlinks may not work as the guide is not part of this portfolio.

find : Finding a Food entry by nutritional value or keyword(s)

(by Eugene)

When you have many entries in the Food Record, it may be rather difficult to search for a particular one. This is where the find command comes in nicely.

The find command shows all Food entries that have a nutritional value matching what you specify. This can be the number of calories, or the number of grams of protein, carbohydrate or fat.

Alternatively, you can choose to search for a keyword which appears in any part of the name or in one of the tags associated with a particular Food entry.

Here are some key pointers:

  • The find command takes in one, and only one parameter.

  • For finding via nutritional value, only Food entries with the same value will be shown.

  • You don’t have to capitalise keywords. The search is always case insensitive when searching for keyword(s) via name or tag.

  • You don’t actually have to type out the entire keyword either. For your convenience, incomplete keywords will be matched to Food entries containing them in any part of the specified parameter of name or tag.

  • You can search for multiple name keywords by using a single n/ Prefix, separating them with space(s).

If you’re a fast typist, fear not! We understand the possibility that typing errors can be made quite often, so any additional input for the find command without a preceding Prefix (e.g. n/, p/) will be ignored.
The Food Record displays the relevant entries of each find command. We can reset the Food Record to show all entries once again using the list command.

Format: find [n/NAME] [cal/CALORIES] [p/PROTEINS] [c/CARBOHYDRATES] [f/FATS] [t/TAG]
(Reminder: choose only 1 parameter)

Examples:

Example 1: Say you want to use Calgo to search for a Food entry with 150 calories because you are looking for a light snack. Here is how you can do it:

FindCommandCalorieBefore

You should type find cal/150, then press enter.

FindCommandCalorieAfter

Once the command has been entered, the Result Display shows the results of your command and the Food Record displays the relevant entries with 150 calories.

Example 2: Say you wish to find entries which contain the keyword Cheese in their name, but your hand slipped and the keyboard only typed Chees. This is what happens:

FindCommandSubstringBefore

You are likely to enter find n/Chees as the command input.

FindCommandSubstringAfter

Once the command has been entered, the Result Display shows the results of your command and the Food Record shows the relevant entries which contain Chees in their name. This is not too bad, as you still obtain entries that will be largely relevant to Cheese. This shows that the find command can search for Food entries with incomplete keywords.

Example 3: Say you are lazy but wish to find entries containing the keyword sweet in their tag. Here is how you can do it:

FindCommandTagBefore

You can type find t/swe as input, and then press enter.

FindCommandTagAfter

Upon entering the command, the Food Record will display all entries which have the swe keyword present in any one of their tags. As you can search using incomplete keywords, the intended search for the sweet tag will also have its result shown.

Please note that the search is case-insensitive, an example being the resulting Sweet tag of Bandung. Moreover, as with Example 2, we allow for incomplete words to be used as search keywords.

export : Exporting the current Food Record into a reference sheet

(by Eugene)

Obtaining a portable copy of the current Food Record may be useful for various purposes. For instance, you can conveniently share your Food entries with friends, print the Food Record for future reference, or even adapt it to suit your personal cooking needs in the kitchen. Whatever the purpose, we have you covered with the export command.

The export command provides you with a neatly formatted, editable file that reflects all entries in the current Food Record. This file (named FoodRecord.txt) will be created in the data/exports folder.

Here are some key pointers for using the command:

  • FoodRecord.txt shows the Food Record in alphabetical order of Food names, and includes the corresponding details of each Food neatly in a table.

  • If a previous FoodRecord.txt file exists, do close any instances of the file (if previously opened) before running the export command. The file will be replaced by an updated version representing your latest Food Record.

  • Tags are placed outside of the dashed lines.

This lets you manually track your diet using a reference sheet of your past Food entries. You can freely edit this reference sheet to include information outside of Calgo.
Certain Food names may be too long to fit into the given space. Such names will be shown on multiple lines. However, rest assured that all your information is still captured and neatly organised. Individual entries will also appear on separate lines.

Format: export
(any parameters entered are ignored)

Example:

Let’s suppose you wish to export the current Food Record so that you can print a copy for reference while cooking. Here’s how you can do it:

ExportCommandBefore

You should type in the command and press enter, as seen above.

ExportCommandAfter

Doing so, Calgo will show you a result message indicating the copy has been successfully generated. You can find this copy (named FoodRecord.txt) in the data/exports folder.

list : Listing all Food in current Food Record

(by Eugene)

With a large number of entries in the Food Record, you may remember that we can use the find command to display only the relevant Food entries. Once we are done with the search, we will eventually want to view all entries again. This is where the list command comes in handy.

The list command resets the display accordingly to show all entries in the Food Record. These entries will be neatly sorted, just as the Food Record previously appeared:

  • All entries will appear in lexicographical order.

  • Food entries will once again be listed with all their corresponding details.

You can think of this as the reverse of a find command. After a find command, you are advised to complete your intended actions first, before using the list command to reset the display. This allows for a smoother workflow as you will now avoid the need to perform the same find command again.

Format: list
(any parameters entered are ignored)

Example:

Let’s say you want to view all entries again after performing a find n/Chicken command. You can do the following:

ListCommandBefore

Type list as input, then press enter.

ListCommandAfter

The Result Display will then indicate the result of your command, and the Food Record will now show all Food entries once again.

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. Please note that some hyperlinks may not work as the guide is not part of this portfolio.

Storage component

StorageClassDiagram
Figure 1. Structure of the Storage Component

API : Storage.java

The Storage component allows us to save FoodRecord, UserPref, Goal, and ConsumptionRecord data in json format onto the disk, and read them back later on during the next session.

This would facilitate the following functions:

  1. Load past user App data and preferences.

  2. Generate and save insights reports based on previously and currently recorded user consumption.

  3. Generate and save a user-friendly version of the accumulated FoodRecord.

Searching for specific Food via categories and substrings

(by Eugene)

This section addresses how the find and list commands work. As they are complementary in their functions during the search process, both find and list commands will be explained together here for better coherence.

The find command allows us to search through the FoodRecord (via categorical or substring search) based on what the user enters for the Prefix. Users may enter one and only one Prefix. The search results can then be displayed in the GUI’s Food Record.

Meanwhile, the list command allows us to reset the GUI’s Food Record to once again show all entries in lexicographical order. This can be thought of as the reverse of a find command. However, unlike the find command, the list command does not use any Prefix, and ignores any input after its command word.

Prefix here indicates which Food attribute we are interested in. Categorical search finds Food objects with values that match the user-specified value representing one of the nutritional categories (Calorie, Protein, Carbohydrate, or Fat). Meanwhile, substring search finds matches for the user-entered substring in any part of the the Name or in any of the Tag objects belonging to the Food objects.
For more information on lexicographical ordering, please refer to its relevant section here.

The above commands rely on the FindCommand and ListCommand objects respectively. Objects of both classes use a Predicate<Food> object to filter through the current Food objects, where Food objects will be displayed in the GUI’s Food Record should they evaluate these predicates to be true.

Implementation

To search via a particular Food attribute, we use a FindCommandParser to create the corresponding Predicate<Food> based on which Food attribute the Prefix entered represents. This predicate is then used to construct a new FindCommand object, which changes the GUI display when executed.

The class diagram below shows the relevant Predicate<Food> classes used in the construction of FindCommand objects.

FindListCommandPredicateClassDiagram
Figure 2. Class Diagram showing the relevant predicates used in constructing FindCommand objects

As seen in the above class diagram, each Predicate<Food> is indeed representative of either Name, Calorie, Protein, Carbohydrate, Fat, or Tag. Moreover, it should be noted that each of these predicates test against a Food object, and therefore have a dependency on Food.

The sequence diagram below demonstrates how the find command works, for both categorical and substring search:

FindSequenceDiagram
Figure 3. Sequence Diagram for find command: Categorical Search and Substring Search
The lifeline for the both of the FindCommandParser objects, and both of the FindCommand objects should end at their destroy markers (X) but due to a limitation of PlantUML, the lifelines reach the end of diagram.

From the above, it is clear that both categorical search and substring search of the find command have similar steps:

Step 1: LogicManager executes the user input, using CalgoParser to realise this is a find command, and a new FindCommandParser object is then created.

Step 2: The FindCommandParser object parses the user-entered arguments that come with the Prefix, creating a Predicate<Food> object based on which Food attribute the Prefix represents.

  • In the above diagram examples, a ProteinContainsKeywordsPredicate object is created for the categorical search via Protein while a NameContainsKeywordsPredicate object is created for the substring search via Name.

Step 3: This Predicate<Food> object is then used to construct a new FindCommand object, returned to LogicManager.

Step 4: LogicManager calls the execute method of the FindCommand created, which filters for Food objects that evaluate the predicate previously created to be true. It then returns a new CommandResult object reflecting the status of the execution. These changes are eventually reflected in the GUI.

The find command therefore searches through the existing FoodRecord and then displays the relevant search results in the GUI’s Food Record. To once again show all Food entries in the display, we use the list command.

In constrast to FindCommand, the ListCommand constructor takes in no arguments and simply uses the predicate Model.PREDICATE_SHOW_ALL_FOODS to always show all Food entries in its execute method. This is described by the sequence diagram below:

ListSequenceDiagram
Figure 4. Sequence Diagram for list command
The lifeline for the ListCommand object should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

How the list command works:

Step 1: LogicManager executes the user input, using CalgoParser to realise this is a list command, and a new ListCommand object is created.

Step 2: LogicManager then calls the execute method of this ListCommand, which uses Model.PREDICATE_SHOW_ALL_FOODS to evaluate to true for all Food objects in the FoodRecord.

Step 3: LogicManager then returns a new CommandResult object to reflect the status of the execution, in the GUI. The GUI’s Food Record reflects the above changes to show all Food entries once again.

Design considerations

Aspect: Predicate construction source.
  • Alternative 1 (current choice): Each Predicate<Food> is constructed using a new object of type either Name, Calorie, Protein, Carbohydrate, Fat, Tag.

    • Pros:

      • Defensive programming by building new objects rather than relying on mutable sources.

      • Can reuse existing code and classes like ArgumentMultimap and their methods.

      • Models objects well to reflect the real-world.

    • Cons:

      • May be more resource-intensive than other alternatives.

      • New developers may not find this intuitive.

  • Alternative 2: Each Predicate<Food> is created using a String which represents the keywords.

    • Pros:

      • Easier to implement with fewer existing dependencies.

      • Less resource-intensive.

    • Cons:

      • More prone to bugs.

      • Difficult to ascertain which Food attribute it actually represents.

      • More difficult to debug as String type is easily modified.

      • Does not reflect good OOP practices

  • Alternative 1 (current choice): Allow substring search for both Name and Tag

    • Pros:

      • Improves user experience.

      • Can reuse common code as the approach for both Name and Tag are similar.

      • Generally easy to implement substring finding.

      • Can use regular expressions if needed, which are powerful and suitable for our purpose.

    • Cons:

      • Requires good understanding of the original project.

      • Need to know the String type, regular expressions, and their implications.

      • Need to implement searching via multiple types of Food attributes and hence introduces more dependencies.

      • Need to implement a new Parser class to detect each relevant Prefix.

  • Alternative 2: Only allow exact word matches for Name and Tag

    • Pros:

      • Can simply reuse large parts of the original project’s existing code.

      • Less prone to bugs.

      • Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.

    • Cons:

      • Diminishes user experience.

      • May not fully satisfy the user requirements.

      • Need to implement searching via multiple types of Food attributes and hence introduces more dependencies.

      • Need to implement a new Parser class to detect each relevant Prefix.

Summary

In essence, this section focuses on searching which is implemented via find and list commands.

The find command performs a categorical search if a value from a nutritional category (Calorie, Protein, Carbohydrate, Fat) is specified. Otherwise, a substring search is performed to find Food objects that contain the entered substring in their Name or in one of their Tag s. These rely on the Predicate<Food> object used in constructing the FindCommand, which depend on the Prefix entered by the user.

Meanwhile, the list command simply uses the predicate already defined in Model to display all Food objects.

The above can be summarised in the activity diagram below:

FindListCommandActivityDiagram
Figure 5. Activity Diagram for Searching

Lexicographical Food order

(by Eugene)

This section addresses how the GUI Food Record entries appear in lexicographical order, which is an effect of sorting Food objects in the FoodRecord.

Over time, users will eventually have many Food entries — these should be sorted for a better experience. Intuitively, the lexicographical order is the most suitable here.

In essence, Food objects are sorted by the UniqueFoodList (which is inside FoodRecord). Sorting is performed each time Food object(s) are newly added to the UniqueFoodList, or during the initialisation of the UniqueFoodList upon App start-up. There is no need to re-sort when a Food object is deleted or edited as the order is maintained.

For a better understanding of adding and editing Food objects using the update command, please refer to its relevant section here.
Although the the list command changes the GUI Food Record display, it does not actually perform sorting. It simply resets the GUI Food Record to show all Food entries, and is usually used after a find command. You can read more about them here.

Implementation

The UniqueFoodList is able to sort Food objects because the Food class implements the Comparable<Food> interface. This allows us to specify the lexicographical order for sorting Food objects via their Name, using the following compareTo method in the Food class:

public int compareTo(Food other) {
    String currentName = this.getName().toString();
    String otherName = other.getName().toString();
    return currentName.compareTo(otherName);
}

How the sorting process works:

  • When the App starts up, a new UniqueFoodList is created from the source json file (if available) or otherwise the default entries, and the created Food objects are sorted as they are added to it.

  • Existing Food objects are therefore arranged in lexicographic order by Name.

  • Thereafter, UniqueFoodList sorts the Food objects whenever new Food objects are added.

It should be noted that sorting is only performed by the addFood and setFoods method of the UniqueFoodList, which calls the sortInternalList method. Not to be confused, the setFood method, which is used when a Food object is edited, does not perform any sorting.

The sequence diagram below shows how the lexicographical ordering is performed when Calgo starts up:

LexicographicalOrderingStartupSequenceDiagram
Figure 6. Lexicographical Ordering Sequence Diagram for App Start-up

Based on the above diagram, when Calgo starts:

Step 1: We initialise the ModelManager object. For this, we use previously stored user data if available (by reading in from the source json files). Otherwise, we use the default Calgo Food entries.

Step 2: Before we can finish constructing a new ModelManager object, we require the creation of a new FoodRecord object which in turn requires the creation of a new UniqueFoodList object.

Step 3: Once UniqueFoodList is constructed, we introduce the initialising data into it using the setFoods method. This calls the sortInternalList method, which sorts the newly added Food objects in the ObservableList<Food> contained in UniqueFoodList, according to the specified lexicographical order (defined in the Food class).

Moving on, the sequence diagram below (which is a reference frame omitting irrelevant update command details) describes the lexicographical sorting process when Food objects are added (not edited) using the update command:

LexicographicalOrderingUpdateSequenceDiagram
Figure 7. Lexicographical Ordering Sequence Diagram for Updating
This is in a reference frame as it is reused in the update section here)

Here, the diagram describes what happens after parsing the user input and creating an UpdateCommand object. Since the Food entered by the user is an entirely new Food object without a Name-equivalent Food existing in the UniqueFoodList:

Step 1: We call the respective addFood and add methods as seen in the diagram, eventually adding the Food object into the UniqueFoodList and arriving at its sortInternalList method call.

Step 2: The sortInternalList method then sorts the Food objects in the ObservableList<Food> contained in UniqueFoodList, according to the specified lexicographical order defined in the Food class.

During an update command, we do not perform sorting if the user enters a Food object that already has an existing counterpart with an equivalent Name in the UniqueFoodList.

Any re-ordering will eventually be reflected in the GUI, facilitated by the following (in the case of an update command) or otherwise something similar:

model.updateFilteredFoodRecord(Model.PREDICATE_SHOW_ALL_FOODS);

Design considerations

Aspect: Frequency of sorting operation.
  • Alternative 1 (current choice): Sort whenever a new Food is added or during App start-up.

    • Pros:

      • Guarantees correctness of sorting.

      • Saves on computational cost by not sorting during deletion or edits as the order is preserved.

      • Computational cost is not too expensive since the introduced Food objects usually come individually rather than as a collection (except during App start-up).

    • Cons:

      • Need to ensure implementations of various commands changing the Model are correct and do not interfere with the sorting process.

      • May be computationally expensive if there are many unsorted Food objects at once, which is possible when Calgo starts up.

  • Alternative 2: Sort only when calling the list command.

    • Pros:

      • Easier to implement with fewer existing dependencies.

      • Uses less computational resources since sorting is only done when list command is called.

    • Cons:

      • Diminishes user experience.

      • May be incompatible with certain Storage functions.

      • May lead to bugs in overall product due to incompatible features.

Aspect: Data structure to store Food objects.
  • Alternative 1 (current choice): Use UniqueFoodList to store all Food objects.

    • Pros:

      • Can reuse existing code, removing the need to maintain a separate list-like data structure.

      • Based on existing code, any changes to the Model from the sorting process are automatically reflected in the GUI. This is very useful for testing and debugging manually.

    • Cons:

      • Many of the underlying ObservableList methods are built-in and cannot be edited. They are also difficult to understand for those unfamiliar. This can make development slightly trickier, especially in following certain software engineering principles.

  • Alternative 2: Use a simpler data structure like an ArrayList.

    • Pros:

      • Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.

    • Cons:

      • More troublesome as we require self-defined methods, abstracted over the existing ones. If not careful, these self-defined methods can possibly contain violations of certain software engineering principles, which may introduce regression in the future.

      • May be inefficient in using resources.

Summary

The UniqueFoodList facilitates the lexicographical ordering of Food objects and hence how their respective entries appear in the GUI Food Record. This can be summarised in the activity diagram below:

LexicographicalOrderingActivityDiagram
Figure 8. Activity Diagram for Lexicographical Ordering

Exporting the current FoodRecord into a portable file

(by Eugene)

This section addresses how the export command works, creating a FoodRecord.txt file showing details of all the Food objects currently stored in the FoodRecord. The information is presently neatly in table form and the file is created in the data/exports folder.

The export command mainly uses an ExportGenerator object to generate the file. All formatting options and methods to write the contents of the file are included in the ExportGenerator class, which extends the DocumentGenerator class.

You may find the report command similar as they both create a new file for the user. You can read more about it here.

Implementation

Most of the work in generating the file is done by the generateExport method of ExportGenerator. You can access the class to view its methods for writing the header and footer components, which are relatively easily to understand.

However, the methods for writing the file body is likely where some explaining is required. Here, the formatting of the table body is determined by the following:

    private static final int NAME_COLUMN_SIZE = 45;
    private static final int VALUE_COLUMN_SIZE = 20;

NAME_COLUMN_SIZE represents the allowed space for the Name. If a Food object has a Name which is too long, the Name will be truncated and continued on the following lines. Meanwhile, VALUE_COLUMN_SIZE represents the allowed space for each nutritional value of Calorie, Protein, Carbohydrate, and Fat in the table. These are guaranteed to be within a length of 5 characters when parsing, and should not exceed the given space.

The nutritional values will always be shown in the first line of their respective Food object after its (possibly truncated) Name. This is facilitated by the printBody method of ExportGenerator, which calls its printBodyComponent method and subsequently its generateFinalisedEntryString method, which performs the truncation and amendment of the Name as necessary.

Moving on, the sequence diagram below demonstrates how the export command works to create the user copy of the current FoodRecord:

ExportSequenceDiagram
Figure 9. Sequence Diagram for export command: Generating FoodRecord.txt
The lifeline for the ExportCommand object and that of the ExportGenerator object should end at their destroy markers (X) but due to a limitation of PlantUML, the lifelines reach the end of diagram.

From the above, creating FoodRecord.txt involves the following steps:

Step 1: LogicManager executes the user input, using CalgoParser to realise this is a export command, and a new ExportCommand object is created.

Step 2: LogicManager then calls the execute method of this ExportCommand object. This results in a call to the Model to get the current FoodRecord, which is used to construct a new ExportGenerator object. The ExportGenerator is responsible for creating the FoodRecord.txt file and writing to it.

Step 3: ExportCommand then calls the generateExport method of ExportGenerator, writing the required parts to the file. This returns a boolean indicating whether the file creation and writing are successful.

Step 4: A new CommandResult object indicating the result of the execution is then constructed and reflected in the GUI.

Design considerations

Aspect: Type of file to create.
  • Alternative 1 (current choice): Create a .txt file to represent the FoodRecord.

    • Pros:

      • Satisfies user requirements by allowing editing of the file to include custom entries.

    • Cons:

      • Need to define new classes and methods for file writing, which may introduce more dependencies.

      • May be more resource-intensive than other alternatives.

      • New developers may be unfamiliar with String manipulation and regular expressions.

  • Alternative 2: Create a .pdf file to represent the FoodRecord

    • Pros:

      • The contents appear to be more legitimate.

      • Can use external libraries for convenience.

      • May be less resource-intensive.

    • Cons:

      • May not satisfy user requirements as the file cannot be edited easily.

      • May introduce more bugs, additional dependencies, and become prone to external factors.

      • More difficult to debug due to lack of familiarity with external libraries.

      • May require more space.

Aspect: Abstraction for ExportGenerator and ReportGenerator.
  • Alternative 1 (current choice): Create DocumentGenerator abstract class which both ExportGenerator and ReportGenerator extends.

    • Pros:

      • Good OOP practice, following its principles.

      • Allows for code reuse and neater code.

      • Able to apply concepts of polymorphism, if required.

      • May be now easier to debug.

    • Cons:

      • Need to define new class, possibly introducing more dependencies.

      • Need to identify what is common to both ExportGenerator and ReportGenerator.

  • Alternative 2: Use an interface which both classes will implement.

    • Pros:

      • Similar to Alternative 1.

    • Cons:

      • Does not allow methods to be defined in the interface. (Some exceptions: default methods, etc)

      • May need to repeat definitions which may be the same for both classes.

  • Alternative 3: Do not use an interface or abstract class.

    • Pros:

      • Requires less effort.

      • Does not introduce additional dependencies.

    • Cons:

      • Unable to reap benefits of the above alternatives.

Summary

In short, this section addresses how users are able to obtain an editable copy of the current FoodRecord using the export command.

The export command largely relies on the ExportGenerator class, which facilitates creating the file and writing to it.

The above can be summarised in the activity diagram below:

ExportActivityDiagram
Figure 10. Activity Diagram for Searching