After studying this code and completing the corresponding exercises, you should be able to,
- Utilize User Stories
[LO-UserStories] - Utilize use cases
[LO-UseCases] - Use Non Functional Requirements
[LO-NFR] - Use Polymorphism
[LO-Polymorphism] - Use abstract classes/methods
[LO-Abstract] - Follow Liskov Substitution Principle
[LO-LSP] - Use Java-FX for GUI programming
[LO-JavaFx] - Apply Dependency Inversion Principle
[LO-DIP] - Use Dependency Injection
[LO-DI] - Apply Open-Closed Principle
[LO-OCP]
- Assume you are planing to expand the functionality of the AddressBook (but keep it as a CLI application).
What other user stories do you think AddressBook should support? Add those user stories to the
DeveloperGuide.md.
- Add a use case to the
DeveloperGuide.mdto cover the case of renaming of an existing tag.
e.g. rename the tagfriendstobuddies(i.e. all persons who had thefriendstag will now have abuddiestag instead)
Assume that AddressBook confirms the change with the user before carrying out the operation.
- Add some more NFRs to the
DeveloperGuide.md
Note how the Command::execute() method shows polymorphic behavior.
- Add an
abstractmethodboolean isMutating()to theCommandclass. This method will returntruefor command types that mutate the data. e.g.AddCommand - Currently, AddressBook data are saved to the file after every command.
Take advantage of the the new method you added to limit file saving to only for command types that mutate data.
i.e.addcommand should always save the data whilelistcommand should never save data to the file.
Note: There may be better ways to limit file saving to commands that mutate data. The above approach, while not optimal, will give you chance to implement a polymorphic behavior.
Covered by [LO-Polymorphism]
- Add a
throws Exceptionclause to theAddCommand::executemethod. Notice how Java compiler will not allow it, unless you add the samethrowsclause to the parent class method. This is because if a child class throws an exception that is not specified by the Parent's contract, the child class is no longer substitutable in place of the parent class. - Also note that while in the above example the compiler enforces LSP, there are other situations where it is up to
the programmer to enforce it. For example, if the method in the parent class works for
nullinput, the overridden method in the child class should not rejectnullinputs. This will not be enforced by the compiler.
Resources
- JavaFX 8 Tutorial by Marco Jakob
- If you are new to JavaFX, follow Marco's tutorial given above.
- Do some enhancements to the AddressBook GUI. e.g. add an application icon, change size/style
- Note how
Logicclass depends on theStorageFileclass. This is a violation of DIP. - Modify the implementation as follows so that both
LogicandStorageFilenow depend on the abstractionStorage.

-
Note how
Logicclass depends on theStorageFileclass. This means when testing theLogicclass, our test cases executes theStorageFileclass as well. What if we want to test theLogicclass without getting theStorageFileclass involved? -
Now, change the implementation as follows so that we can inject a
StorageStubwhen testing theLogicclass.

If you did the exercise in
LO-DIPalready but those changes are in a different branch, you may be able to reuse some of those commits by cherry picking them from that branch to the branch you created for this exercise.
Note: cherry picking is simply copy-pasting a commit from one branch to another. In SourceTree, you can right-click on the commit your want to copy to the current branch, and choose 'Cherry pick' -
Implement the
StorageStubto ignore calls to thesavemethod. Update theLogicTestto work with theStorageStubinstead of the actualStorageFileobject.
- Add a new command to the Address Book. e.g. an
editcommand - Notice how little you need to change in the
Logicclass that is responsible for executing the commands. That is because classesLogicand*Commandfollow the OCP i.e.Logicis open to be extended with more commands but closed for modifications. - Think about how to make the
Personclass similarly open to be extended with more contact details (e.g.SkypeId) without needing modifications to its code during those extensions.