After studying this code and completing the corresponding exercises, you should be able to,
- Apply Encapsulation
[LO-Encapsulation]
- Implement a class
[LO-ImplementClass]
- Follow the Single Responsibility Principle
[LO-SRP]
- Handle Exceptions
[LO-Exceptions]
- Use Inheritance to achieve code reuse
[LO-Inheritance]
- Follow Interface Segregation Principle
[LO-ISP]
- Use class-level members
[LO-ClassLevel]
- Use Composition
[LO-Composition]
- Use Association Classes
[LO-AssociationClass]
- Use JUnit to implement unit tests
[LO-JUnit]
- Use TDD
[LO-TDD]
- Work in a 2KLoC code base
[LO-2KLoC]
- 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.
- Code smell: Primitive Obsession - 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.
- Update the user guide and tests to match.
The Single Responsibility Principle (SRP) states that a class should have only one reason to change. The code given follows SRP to a reasonable extent, but there are places where it can be applied further.
- An explanation of the SRP from www.oodesign.com
- Another explanation (more detailed) by Patkos Csaba
- A book chapter on SRP by Robert C. Martin
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 namedFormatter
.
- Best Practices for Exception Handling by Gunjan Doshi
- The current code does not handle the situation where the user deletes the storage file while the AddressBook program is running. Use exceptions to handle that situation.
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.
- Extract commonalities from
Phone
,Email
andAddress
classes into a parent class calledContact
.
The Interface-Segregation Principle (ISP) states that no client should be forced to depend on methods it does not use.
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.
-
Override
thegetPrintableString
in classesName
,Phone
,Email
, andAddress
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());
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(...)
- Convert the
Parser::parseCommand(...)
method (i.e. theparseCommand()
method of theParser
class) to a class-level method. Note how this method can be either class-level or instance-level. - Note how the
setTags
method of thePerson
class cannot be converted to a class-level method. - Add an instance-level member
int sequenceNumber
and a class-level variableint nextSequenceNumber
to thePerson
class. Using these two variables, ensure that eachPerson
object has a unique sequence number that indicates the order in whichPerson
objects were created. e.g.Adam
is the firstPerson
object to be created. It is assigned sequence number 1.Ben
andCharlie
are created next, and assigned 2 and 3 respectively.Ben
is deleted.Daisy
is added next and is given sequence number 4.
Note the following examples of composition (filled diamond):
Whole | Parts |
---|---|
AddressBook |
UniquePersonList UniqueTagList |
Person |
Name Phone Email Address |
Contrast with these examples of aggregration (empty diamond):
Container | Contained |
---|---|
UniquePersonList |
Person |
UuniqueTagList |
Tag |
The current design does not have any association classes.
-
Assume the following:
- There are commands to add and remove tags to a person in the address book.
- 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.
EachTagging
object will represent an adding or deleting of a tag for a specific person that happened during that session.
Note that if the list of
Tagging
s is kept as a class-level variable theTagging
class, the diagram would be like this:
Note the test/seedu/addressbook/parser/ParserTest.java
class that users Junit to implement automated unit tests.
- JUnit cookbook - a short tutorial from JUnit creators
- JUnit tutorial - a more detailed tutorial from a developer Lars Vogel
- How do I run all JUnit tests at once?
- How to test private methods in Java? [ short answer ] [ long answer ]
- [Quora post] What is the best way to avoid bugs
- [Web article] The three pillars of unit testing - A short article about what makes a good unit test.
- [Quora post] Is automated testing relevant to startups?
- Learning from Apple’s #gotofail Security Bug - How unit testing (and other good coding practices) could have prevented a major security bug.
- First, make sure you know how to run JUnit tests by running the
ParserTest.java
. Instructions are in the Developer Guide. - Add a
test/seedu/addressbook/common/UtilsTest.java
containing JUnit tests for theUtils
class.
It's recommended you do [LO-JUnit]
before attempting TDD.
- Uncle Bob’s three rules of TDD - (Uncle Bob = Robert C. Martin, the author of Clean Code)
- Let’s play TDD screencasts by James Shore: [ episode 1 ] [ episode 2 ] [ the rest ]
- TDD: Is There Really Any Debate Any Longer?
- [Dev opinion]Programmers Without TDD Will be Unemployable by 2022
-
Add the following method to the
Name
class. Use the TDD technique to add the method. Commit after each step./** * Returns true of 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.
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