Apply Encapsulation [LO-Encapsulation]

Exercise: Encapsulate CommandResult class members

  • A member of the CommandResult class is not encapsulated. i.e. it is visible outside the object. Hide it so that it can only be accessed using methods provided.


Implement a class [LO-ImplementClass]

Exercise: Split Address into more classes

Side reading: Code smell: Primitive Obsession - A case against using primitives instead of small objects for simple tasks

  • Assume the address is entered in the following format a/BLOCK, STREET, UNIT, POSTAL_CODE e.g. a/123, Clementi Ave 3, #12-34, 231534

  • Split the Address class as follows. Note: the filled diamond symbol ◆ means an Address consists of Block, Street, etc.

    AddressClasses
  • Update the user guide and tests to match.


Follow the Single Responsibility Principle [LO-SRP]

Exercise: Split TextUi class

The exercise in the LO-ImplementClass section is somewhat related to SRP as well. Here’s a slightly more difficult exercise.

  • TextUi class has more than one responsibility. Try to extract out the responsibility of Formatting text for display (e.g. adding decorations) in to a separate class named Formatter.


Handle Exceptions [LO-Exceptions]

Exercise: Handle 'file readonly' situation

  • The current code does not handle the situation where the user accidentally makes the storage file read only while the AddressBook program is running. Use exceptions to handle that situation better. e.g. instead of crashing, the program can inform the user of the problem.


Use Inheritance to achieve code reuse [LO-Inheritance]

Note how the Command class contains some code that is reused by some of its child classes. By defining *Command classes as child classes of Command, we have avoided having to duplicate those methods in multiple *Command classes.

Exercise: Extract a Contact class

  • Extract commonalities from Phone, Email and Address classes into a parent class called Contact.

    ContactClassHierarchy

Follow Interface Segregation Principle [LO-ISP]

Note how the Person class implements the ReadOnlyPerson interface so that clients who don’t need write access to Person objects can access Person objects through the ReadOnlyPerson interface instead.

ReadOnlyPersonUsage

Exercise: Add a Printable interface

  • Add a Printable interface as follows.

    PrintableInterface
  • Override the getPrintableString in classes Name, Phone, Email, and Address so that each produces a printable string representation of the object. e.g. Name: John Smith, Phone: 12349862

  • Add the following method in a suitable place of some other class. Note how the method depends on the Interface.

    /**
      * Returns a concatenated version of the printable strings of each object.
      */
    String getPrintableString(Printable... printables){

    The above method can be used to get a printable string representing a bunch of person details. For example, you should be able to call that method like this:

    //p is a Person object
    return getPrintableString(p.getPhone(), p.getEmail(), p.getAddress());

Use class-level members [LO-ClassLevel]

Note how some of the variables and methods are declared static. That means they are class-level members rather than instance-level members. e.g. Main.VERSION, Name.EXAMPLE, Utils.isAnyNull(…​)

Exercise: Add class-level members

  • Convert the Parser::parseCommand(…​) method (i.e. the parseCommand() method of the Parser class) to a class-level method. Note how this method can be either class-level or instance-level.

  • Note how some instance-level methods, such as the setTags method of the Person class, cannot be converted to a class-level method.

  • Add an instance-level member int sequenceNumber and a class-level variable int nextSequenceNumber to the Person class. Using these two variables, ensure that each Person object has a unique sequence number that indicates the order in which Person objects were created. e.g.

    • Adam is the first Person object to be created. It is assigned sequence number 1.

    • Ben and Charlie are created next, and assigned 2 and 3 respectively.

    • Ben is deleted.

    • Daisy is added next and is given sequence number 4.


Use Composition [LO-Composition]

Note the following examples of composition (filled diamond):

Whole Parts

AddressBook

UniquePersonList

Person

Name Phone Email Address Tag

Contrast with these examples of aggregration (empty diamond):

Container Contained

UniquePersonList

Person

Use Association Classes [LO-AssociationClass]

The current design does not have any association classes.

Exercise: Add an Association Class Tagging

  • Assume the following:

    1. There are commands to add and remove tags to a person in the address book.

    2. When the AddressBook program exits, it should print out a list of all the tags added/deleted during that session. e.g.

      + Jake Woo [friend]
      - Jake Woo [colleague]
      + Jean Wong [client]
  • To support (ii) above, implement an Association Class called Tagging as given in the diagram below. Each Tagging object will represent an adding or deleting of a tag for a specific person that happened during that session.

    TaggingClass

    Note that if the list of Tagging's is kept as a class-level variable in the Tagging class, the diagram would be like this:

    TaggingsInTagging

Use JUnit to implement unit tests [LO-JUnit]

References

Note how there are many test classes in this code base that uses JUnit to implement automated unit tests e.g. test/java/seedu/addressbook/parser/ParserTest.java class contains tests for the seedu.addressbook.parser.Parser class.

Exercise: Write unit tests for the Utils#isAnyNull(Object…​) method


Use TDD [LO-TDD]

It’s recommended you do [LO-JUnit] before attempting TDD.

Exercise: Add a method in TDD fashion

  • Add the following method to the Name class. Use the TDD technique to add the method. Commit after each step.

    /**
      * Returns true if the other name is very similar to this name.
      * Two names are considered similar if ...
      */
      public boolean isSimilar(Name other) { ... }
  • You may define 'similar' as you see fit. Make sure the definition covers scenarios where other name is null, in a different case, in a different order, is a subset/superset, etc. e.g. John K Smith John K SMITh John Smith Smith, John K

  • Don’t forget to refactor the method to improve its code quality at the end.


Work in a 2KLoC code base [LO-2KLoC]

Exercise: Enhance AddressBook

Add a feature to AddressBook. Here are some suggestions.

  • An Edit command

  • A Sort command

  • List all persons automatically after an add or delete command

  • Any other enhancement that you might see fit