## Object-Oriented Design & Patterns

#### Cay S. Horstmann

### Chapter 3

### The Object-Oriented Design Process

## Chapter Topics

- An overview of the Date classes in the Java library
- Designing a Day class
- Three implementations of the Day class
- The importance of encapsulation
- Analyzing the quality of an interface
- Programming by contract
- Unit testing

## Date Classes in Standard Library

- Many programs manipulate dates such as
  "Saturday, February 3, 2001"
- `Date` class:

      Date now = new Date();
         // constructs current date/time
      System.out.println(now.toString());
         // prints date such as
         // Sat Feb 03 16:34:10 PST 2001

- `Date` class encapsulates point in time

## Methods of the `Date` class

`boolean after(Date other)` | Tests if this date is after the specified date
`boolean before(Date other)` | Tests if this date is before the specified date
`int compareTo(Date other)` | Tells which date came before the other
`long getTime()` | Returns milliseconds since the epoch (1970-01-01 00:00:00 GMT)
`void setTime(long n)` | Sets the date to the given number of milliseconds since the epoch

## Methods of the `Date` class

- Deprecated methods omitted
- `Date` class methods supply total ordering on `Date` objects
- Convert to scalar time measure
- Note that `before/after` not strictly necessary
  (Presumably introduced for convenience)

## Points in Time

## The `GregorianCalendar` Class`
`

- The `Date` class doesn't measure months, weekdays, etc.
- That's the job of a calendar
- A calendar assigns a name to a point in time
- Many calendars in use:

- Gregorian
- Contemporary: Hebrew, Arabic, Chinese
- Historical: French Revolutionary, Mayan

## Date Handling in the Java Library

## Designing a `Day` Class

- Custom class, for teaching/learning purpose
- Use the standard library classes, not this class, in your own programs
- `Day` encapsulates a day in a fixed location
- No time, no time zone
- Use Gregorian calendar

## Designing a `Day` Class

- Answer questions such as

- How many days are there between now and the end of the year?
- What day is 100 days from now?

## Designing a `Day` Class

- `daysFrom` computes number of days between two days:

      int n = today.daysFrom(birthday);

- `addDays` computes a day that is some days away from a given day:

      Day later = today.addDays(999);

- Mathematical relationship:

      d.addDays(n).daysFrom(d) == n
      d1.addDays(d2.daysFrom(d1)) == d2

- Clearer when written with "overloaded operators":

      (d + n) - d == n
      d1 + (d2 - d1) == d2

- Constructor `Date(int year, int month, int date)`
- `getYear`, `getMonth`, `getDate` acccesors

## Implementing a `Day` Class

- Straightforward implementation:

      private int year
      private int month
      private int date

- `addDays`/`daysBetween` tedious to implement

- April, June, September, November have 30 days
- February has 28 days, except in leap years it has 29 days
- All other months have 31 days
- Leap years are divisible by 4, except after 1582, years divisible by 100 but not 400 are not leap years
- There is no year 0; year 1 is preceded by year -1
- In the switchover to the Gregorian calendar, ten days were dropped: October 15, 1582 is preceded by October 4

## Implementing a `Day` Class

- Ch3/code/day1/Day.java
- Ch3/code/day1/DayTest.java
- Note private helper methods
- Computations are inefficient: a day at a time

## Second Implementation

- For greater efficiency, use Julian day number
- Used in astronomy
- Number of days since Jan. 1, 4713 BCE
- May 23, 1968 = Julian Day 2,440,000
- Greatly simplifies date arithmetic
- Ch3/code/day2/Day.java

## Third Implementation

- Now constructor, accessors are inefficient
- Best of both worlds: Cache known Julian, y/m/d values
- Ch3/code/day3/Day.java
- Which implementation is best?

## The Importance of Encapsulation

- Even a simple class can benefit from different implementations
- Users are unaware of implementation
- Public instance variables would have blocked improvement

- Can't just use text editor to replace all

      d.year

  with

      d.getYear()

- How about

      d.year++?

-     d = new Day(d.getDay(), d.getMonth(), d.getYear() + 1)

- Ugh--that gets really inefficient in Julian representation

- Don't use public fields, even for "simple" classes

## Accessors and Mutators

- Mutator: Changes object state
- Accessor: Reads object state without changing it
- `Day` class has no mutators!
- Class without mutators is immutable
- `String` is immutable
- `Date` and `GregorianCalendar` are mutable

## Don't Supply a Mutator for every Accessor

- `Day` has `getYear`, `getMonth`, `getDate` accessors
- `Day` does not have `setYear`, `setMonth`,`setDate` mutators
- These mutators would not work well

- Example:

      Day deadline = new Day(2001, 1, 31);
      deadline.setMonth(2); // ERROR
      deadline.setDate(28);

- Maybe we should call `setDate` first?

      Day deadline = new Day(2001, 2, 28);
      deadline.setDate(31); // ERROR
      deadline.setMonth(3);

- `GregorianCalendar` implements confusing rollover.

- Silently gets the wrong result instead of error.

- Immutability is useful

## Sharing Mutable References

- References to immutable objects can be freely shared
- Don't share mutable references
- Example

      class Employee
      {
         . . .
         public String getName() { return name; }
         public double getSalary() { return salary; }
         public Date getHireDate() { return hireDate; }
         private String name;
         private double salary;
         private Date hireDate;
      }

## Sharing Mutable References

- Pitfall:

      Employee harry = . . .;
      Date d = harry.getHireDate();
      d.setTime(t); // changes Harry's state!!!

- Remedy: Use clone

      public Date getHireDate()
      {
         return (Date)hireDate.clone();
      }

## Sharing Mutable References

## Final Instance Fields

- Good idea to mark immutable instance fields as final
  private final int day;
- final object reference can still refer to mutating object
  private final ArrayList elements;
- elements can't refer to another array list
- The contents of the array list can change

## Separating Accessors and Mutators

- If we call a method to access an object, we don't expect the object to mutate
- Rule of thumb:
  Mutators should return `void`
- Example of violation:

      StringTokenizer t = . . .;
      String s = t.nextToken();

- Yields current token and advances iteration
- What if I want to read the current token again?

## Separating Accessors and Mutators

- Better interface:

      String getToken();
      void nextToken();

- Even more convenient:

      String getToken();
      String nextToken(); // returns current

- Refine rule of thumb:
  Mutators can return a convenience value, provided there is also an accessor to get the same value

## Side Effects

- Accessor: no change to object
- Mutator: changes object state
- Side effect: change to another object

- Parameter variable
- static object

- Avoid side effects--they confuse users
- Good example, no side effect:

      a.addAll(b)

  mutates `a` but not `b`

## Side Effects

- Date formatting (basic):

      SimpleDateFormat formatter = . . .;
      String dateString = "January 11, 2012";
      Date d = formatter.parse(dateString);

- Advanced:

      FieldPosition position = . . .;
      Date d = formatter.parse(dateString, position);

- Side effect: updates `position`
- Design could be better: add position to formatter state

## Side Effects

- Avoid modifying static objects
- Example: `System.out`
- Don't print error messages to `System.out`:

      if (newMessages.isFull())
         System.out.println("Sorry--no space");

- Your classes may need to run in an environment without `System.out`
- Rule of thumb: Minimize side effects

## Law of Demeter

- Example: Mail system in chapter 2
  `Mailbox currentMailbox = mailSystem.findMailbox(...);`
- Breaks encapsulation
- Suppose future version of `MailSystem` uses a database
- Then it no longer has mailbox objects
- Common in larger systems
- Karl Lieberherr: Law of Demeter
- Demeter = Greek goddess of agriculture, sister of Zeus

## Law of Demeter

- The law: A method should only use objects that are

- instance fields of its class
- parameters
- objects that it constructs with `new`

- Shouldn't use an object that is returned from a method call
- Remedy in mail system: Delegate mailbox methods to mail system
  `mailSystem.getCurrentMessage(int mailboxNumber);
  mailSystem.addMessage(int mailboxNumber, Message msg);
  . . .`
- Rule of thumb, not a mathematical law

## Quality of Class Interface

- Customers: Programmers using the class
- Criteria:

- Cohesion
- Completeness
- Convenience
- Clarity
- Consistency

- Engineering activity: make tradeoffs

## Cohesion

- Class describes a single abstraction
- Methods should be related to the single abstraction
- Bad example:

      public class Mailbox
      {
         public addMessage(Message aMessage) { ... }
         public Message getCurrentMessage() { ... }
         public Message removeCurrentMessage() { ... }
         public void processCommand(String command) { ... }
         ...
      }

## Completeness

- Support operations that are well-defined on abstraction
- Potentially bad example: `Date
  `

      Date start = new Date();
      // do some work
      Date end = new Date();

- How many milliseconds have elapsed?
- No such operation in `Date` class
- Does it fall outside the responsibility?
- After all, we have `before`, `after`, `getTime`

## Convenience

- A good interface makes all tasks possible . . . and common tasks simple
- Bad example: Reading from `System.in`
- Why doesn't `System.in` have a `readLine` method?
- After all, `System.out` has `println`.
- Why can't I make a `BufferedReader` from an input stream?
- After all, I can make a `PrintWriter` from an output stream.

## Clarity

- Confused programmers write buggy code
- Bad example: Removing elements from `LinkedList`
- Reminder: Standard linked list class

      LinkedList countries = new LinkedList();
      countries.add("A");
      countries.add("B");
      countries.add("C");

- Iterate through list:

      ListIterator iterator = countries.listIterator();
      while (iterator.hasNext())
         System.out.println(iterator.next());

## Clarity

- Iterator between elements
- Like blinking caret in word processor
- `add` adds to the left of iterator (like word processor):
- Add X before B:

      ListIterator iterator = countries.listIterator(); // |ABC
      iterator.next(); // A|BC
      iterator.add("France"); // AX|BC

- To remove first two elements, you can't just "backspace"
- `remove` does not remove element to the left of iterator
- From API documentation:
  Removes from the list the last element that was returned by next or previous. This call can only be made once per call to next or previous. It can be made only if add has not been called after the last call to next or previous.
- Huh?

## Consistency

- Related features of a class should have matching

- names
- parameters
- return values
- behavior

- Bad example:

      new GregorianCalendar(year, month - 1, day)

- Why is `month` 0-based?

## Consistency

- Bad example: `String` class

      s.equals(t) / s.equalsIgnoreCase(t)

- But

      boolean regionMatches(int toffset,
         String other, int ooffset, int len)
      boolean regionMatches(boolean ignoreCase, int toffset,
         String other, int ooffset, int len)

- Why not `regionMatchesIgnoreCase`?
- Very common problem in student code

## Programming by Contract

- Spell out responsibilities

- of caller
- ofimplementor

- Increase reliability
- Increase efficiency

## Preconditions

- Caller attempts to remove message from empty `MessageQueue`
- What should happen?
- `MessageQueue` can declare this as an error
- `MessageQueue` can tolerate call and return dummy value``
- What is better?

## Preconditions

- Excessive error checking is costly
- Returning dummy values can complicate testing
- Contract metaphor

- Service provider must specify preconditions
- If precondition is fulfilled, service provider must work correctly
- Otherwise, service provider can do anything

- When precondition fails, service provider may

- throw exception
- return false answer
- corrupt data

## Preconditions

    /**
       Remove message at head
       @return the message at the head
       @precondition size() > 0
    */
    Message removeFirst()
    {
       return (Message)elements.remove(0);
    }

- What happens if precondition not fulfilled?
- `IndexOutOfBoundsException`
- Other implementation may have different behavior

## Circular Array Implementation

- Efficient implementation of bounded queue
- Avoids inefficient shifting of elements
- Circular: head, tail indexes wrap around
- Ch3/queue/MessageQueue.java

## Inefficient Shifting of Elements

## A Circular Array

## Wrapping around the End

## Preconditions

- In circular array implementation, failure of `remove` precondition corrupts queue!
- Bounded queue needs precondition for `add`
- Naive approach:
  `@precondition size() < elements.length`
- Precondition should be checkable by caller
- Better:
  `@precondition size() < getCapacity()`

## Assertions

- Mechanism for warning programmers
- Can be turned off after testing
- Useful for warning programmers about precondition failure
- Syntax:

      assert condition;
      assert condition : explanation;

- Throws `AssertionError` if condition false and checking enabled

## Assertions

    public Message removeFirst()
    {
       assert count > 0 : "violated precondition size() > 0";
       Message r = elements[head];
       . . .
    }

- During testing, run with

      java -enableassertions MyProg

## Exceptions in the Contract

    /**
       . . .
       @throws IllegalArgumentException if queue is empty
    */
    public Message removeFirst()
    {
       if (count == 0)
          throw new IllegalArgumentException();
       Message r = elements[head];
       . . .
    }

- Exception throw part of the contract
- Caller can rely on behavior
- Exception throw not result of precondition violation
- This method has no precondition

## Postconditions

- Conditions that the service provider guarantees
- Every method promises description, `@return`
- Sometimes, can assert additional useful condition
- Example: `add` method

      @postcondition size() > 0

- Postcondition of one call can imply precondition of another:

      q.add(m1);
      m2 = q.remove();

## Class Invariants

- Condition that is

- true after every constructor
- preserved by every method
  (if it's true before the call, it's again true afterwards)

- Useful for checking validity of operations

## Class Invariants

- Example: Circular array queue

      0 <= head && head < elements.length

- First check it's true for constructor

- Sets `head = 0`
- Need precondition `size > 0`!

- Check mutators. Start with `add`

- Sets `headnew = (headold + 1) % elements.length`
- We know `headold` > 0 (Why?)
- `%` operator property:

      0 <= headnew && headnew < elements.length

- What's the use? Array accesses are correct!

      return elements[head];

## Class Invariants

- Example: Queue with array list
  `elements` only contains objects of type `Message`
- Ch2/mail/MessageQueue.java
- It's true for constructor.
  `elements` is initially empty
- Check mutators. Start with `add`
  Parameter type is `Message`
- What's the use? Casts are correct!
  `return (Message)elements.remove(0);`

## Unit Testing

- Unit test = test of a single class
- Design test cases during implementation
- Run tests after every implementation change
- When you find a bug, add a test case that catches it

## JUnit

- Test class name = tested class name + Test
- Test methods start with test

    import junit.framework.*;
    public class DayTest extends TestCase
    {
       public void testAdd() { ... }
       public void testDaysBetween() { ... }
       . . .
    }

## JUnit

- Each test case ends with assertion
- Test framework catches assertion failures

    public void testAdd()
    {
       Day d1 = new Day(1970, 1, 1);
       int n = 1000;
       Day d2 = d1.addDays(n);
       assert d2.daysFrom(d1) == n;
    }