/*
--- CSU213 Spring 2006 Lecture Notes ---------
Copyright 2006  Viera K. Proulx

Lecture 11: Don't be a Copy Cat!

Goals:

 - Learning to design abstract classes and methods within them

Introduction:

Looking at the following data definition (represented as a class diagram)
it is clear that there is a great deal of -- we hope unnecessary -- 
repetition. The design recipe for abstractions tells us to identify the
commonalities and abstract over them. This may look like extra work, but 
sometimes it is actually necessary. 

                         +---------+                          
                         | LibItem |                          
                         +---------+                          
                             / \                              
                              |                               
         - - - - - - - - - - - - - - - - - - - - - -       
         |                     |                   |          
  +---------------+    +---------------+    +--------------+  
  | Book          |    | CD            |    | Magazine     |  
  +---------------+    +---------------+    +--------------+  
  | int catNo     |    | int catNo     |    | int catNo    |  
  | String title  |    | String title  |    | String title |  
  | int dueDate   |    | int dueDate   |    | int dueDate  |  
  | String author |    | String artist |    | int vol      |  
  +---------------+    +---------------+    | int no       |  
                                            +--------------+  
                                                                                                                            

Consider the following problem. You are trying to sort the library items
by their catalog number. You need to be able to compare two library
items and detgermine whether the catalog number of one of then is smaller
than the catalog number of the other one. This calls for the following 
purpose statement and method header:

// is the catalog number of this item lower that the catalog number
// of the given item?
boolean smallerThan(LibItem that){...}

However, we have no instances of LibItems. So, we add the method to every
class that implements LIbItem interface. In the class Book we look at the
template:

... this.catNo ...              -- int
... this.title ...              -- String
... this.dueDate ...            -- int
... this.author ...             -- String

and, of course, we have ... that ..., but the only thing we know about it
is that it is a LibItem. We could use the visitor pattern we have used when
comparing shapes in the lab and add a method with the header

// does this library item has a catalog number greater than the given number?
boolean greaterCatNo(int catNo)

and then the body of the original method would be:

// is the catalog number of this item lower that the catalog number
// of the given item?
boolean smallerThan(LibItem that){
  return that.greaterCatNo(this.catNo);
}

but both of these methods would have to appear in all classes that implement
the LibItem interface!!!!!

Instead, we woould like to indicate that the LIbItem classes have something
more in common, namely fields --- and possibly some methods as well. To do 
this we use 'inheritance' - a common super class that is extended by several 
subclasses. We also say that the subclasses extend the super class.

The super class can declare fields and methods, just like any other class. 
If all methods can be defined in the super class, it becomes a class just
like any other. So, we can modify the class definitions as follows:

                     +--------------+                     
                     | LibItem      |                     
                     +--------------+                     
                     | int catNo    |                     
                     | String title |                     
                     | int dueDate  |                     
                     +--------------+                     
                           / \                            
                           ---                            
                            |                             
          ----------------------------------------        
          |                    |                 |        
  +---------------+    +---------------+    +----------+  
  | Book          |    | CD            |    | Magazine |  
  +---------------+    +---------------+    +----------+  
  | String author |    | String artist |    | int vol  |  
  +---------------+    +---------------+    | int no   |  
                                            +----------+  
                                                          
 We change the shape of the arrows - the inheritance arrow in the 
official UML is shown as a solid line with an open triangle at the end,
as opposed to the implementation arrow that is made of dashed lines
and an open arrow.

The classes are now defined as follows:

// to represent a library item
abstract class LibItem {
  int catNo;
  String title;
  int dueDate;
}

// to represent a book in a library
class Book extends LibItem {
  String author;

  Book(int catNo, String title, int dueDate, String author) {
    this.catNo = catNo;
    this.title = title;
    this.dueDate = dueDate;
    this.author = author;
  }
}

// to represent a CD in a library
class CD extends LibItem {
  String artist;

  CD(int catNo, String title, int dueDate, String artist) {
    this.catNo = catNo;
    this.title = title;
    this.dueDate = dueDate;
    this.artist = artist;
  }
}

// to represent a magazine in a library
class Magazine extends LibItem {
  int vol;
  int no;

  Magazine(int catNo, String title, int dueDate, int vol, int no) {
    this.catNo = catNo;
    this.title = title;
    this.dueDate = dueDate;
    this.vol = vol;
    this.no = no;
  }
}

---------------------------------------------------------------------------
---------------------------------------------------------------------------
---------------------------------------------------------------------------
Alternately, we can lift into the abstract class most of the work
done by the constructor:

// to represent a library item
abstract class LibItem {
  int catNo;
  String title;
  int dueDate;

  LibItem(int catNo, String title, int dueDate) {
    this.catNo = catNo;
    this.title = title;
    this.dueDate = dueDate;
  }
}

// to represent a book in a library
class Book extends LibItem {
  String author;

 Book(int catNo, String title, int dueDate, String author) {
    super(catNo, title, dueDate);
    this.author = author;
  }
}

// to represent a CD in a library
class CD extends LibItem {
  String artist;

  CD(int catNo, String title, int dueDate, String artist) {
    super(catNo, title, dueDate);
    this.artist = artist;
  }
}

// to represent a magazine in a library
class Magazine extends LibItem {
  int vol;
  int no;

  Magazine(int catNo, String title, int dueDate, int vol, int no) {
    super(catNo, title, dueDate);
    this.vol = vol;
    this.no = no;
  }
}
---------------------------------------------------------------------------
---------------------------------------------------------------------------
---------------------------------------------------------------------------

The constructor in the classes Book, CD, and Magazine first calls the 
'super' constructor and initializes the three common fields, then proceeds
to initialize the remaining fields. The super constructor call --must be--
the first line in the body of the constructor in the subclass.

We now continue to desing the method smallerThan. Recall the method header:

// is the catalog number of this item lower that the catalog number
// of the given item?
boolean smallerThan(LibItem that){...}

Some examples are:

Book bk = new Book(123, "Beaches", 45, "PC");
CD cd = new CD(234, "Pearl", 40, "JJ");
Magazine mw = new Magazine(143, "MW", 20, 4);

bk.smallerThan(cd) ---> should be true
mw.smallerThan(bk) ---> should be false

In the LibItem class we have the following template:

... this.catNo ...              -- int
... this.title ...              -- String
... this.dueDate ...            -- int

... that.catNo ...              -- int
... that.title ...              -- String
... that.dueDate ...            -- int

The rest is easy:

// is the catalog number of this item lower that the catalog number
// of the given item?
boolean smallerThan(LibItem that){
  return this.catNo < that.catNo;
}

--- and, of course we run the tests.

We can similarly commpute the number of days an item os overdue.
We let you to work on the design of the following method:

// produce the number of days this book is overdue on the given day
// remember, this cannot be less than zero
int daysOverdue(int today){...}

Note, that we  decided to use a single integer to represent the date
as the number of days since the library opened - to eliminate the
worries about months and days and years.

We now want the method that computes the fine, based on the number 
of days an item is overdue. Here are the specifications:

-- we charge 10 cents per day for each book, but at most $20
-- we charge 50 cents per day for each CD, but at most $20
-- we charge 30 cents per day for each magazine, 
      and there is no limit

It is clear we need three different methods, one for each class.
In the LibItem class we can only include the method header. To indicate
that there is no method body followint the header, we declare the method 
to be 'abstract'. The net effect is similar to what we had seen with 
interfaces - it is a contract mandating that every class that extends
this super class must either define this method, or declare it
as abstract and be an abstract class. Any class that contains an
abstract method must be abstract. 

Here is the LibItem class:

// to represent a library item
abstract class LibItem {
  int catNo;
  String title;
  int dueDate;

  LibItem(int catNo, String title, int dueDate) {
    this.catNo = catNo;
    this.title = title;
    this.dueDate = dueDate;
  }

  // is the catalog number of this item lower that the catalog number
  // of the given item?
  boolean smallerThan(LibItem that){
    return this.catNo < that.catNo; 
  }

  // compute the fine for this library item on the given day
  abstract int fine(int today);
}

The methods in the three subclasses can then be designed by follwing
the design recipe. 

Complete working code is included below.

*/

// to represent a library item
abstract class LibItem {
  int catNo;
  String title;
  int dueDate;

  LibItem(int catNo, String title, int dueDate) {
    this.catNo = catNo;
    this.title = title;
    this.dueDate = dueDate;
  }

  // is the catalog number of this item lower that the catalog number
  // of the given item?
  boolean smallerThan(LibItem that){
    return this.catNo < that.catNo;
  }

  // compute the number of days this library item is overdue
  int daysOverdue(int today){
    return Math.max(0, today - this.dueDate);
  }
}

// to represent a book in a library
class Book extends LibItem {
  String author;

 Book(int catNo, String title, int dueDate, String author) {
    super(catNo, title, dueDate);
    this.author = author;
  }

  int fine(int today){
    return Math.min(2000, 10 * this.daysOverdue(today));
  }
}

// to represent a CD in a library
class CD extends LibItem {
  String artist;

  CD(int catNo, String title, int dueDate, String artist) {
    super(catNo, title, dueDate);
    this.artist = artist;
  }

  int fine(int today){
    return Math.min(2000, 50 * this.daysOverdue(today));
  }
}

// to represent a magazine in a library
class Magazine extends LibItem {
  int vol;
  int no;

  Magazine(int catNo, String title, int dueDate, int vol, int no) {
    super(catNo, title, dueDate);
    this.vol = vol;
    this.no = no;
  }

  int fine(int today){
    return 30 * this.daysOverdue(today);
  }
}

class Examples {
  Examples() {}

  Book bk = new Book(123, "Beaches", 45, "PC");
  CD cd = new CD(234, "Pearl", 40, "JJ");
  Magazine mw = new Magazine(143, "MW", 20, 14, 3);

  // tests for the method smallerThan
  boolean testSmallerThan1 = bk.smallerThan(cd) == true;
  boolean testSmallerThan2 = mw.smallerThan(bk) == false;

  // tests for the method daysOverdue
  boolean testdaysOverdue1 = bk.daysOverdue(48) == 3;
  boolean testdaysOverdue2 = bk.daysOverdue(40) == 0;
  boolean testdaysOverdue3 = cd.daysOverdue(48) == 8;
  boolean testdaysOverdue4 = cd.daysOverdue(30) == 0;
  boolean testdaysOverdue5 = mw.daysOverdue(48) == 28;
  boolean testdaysOverdue6 = mw.daysOverdue(18) == 0;

  // tests for the method fine
  boolean testdaysFine1 = bk.fine(48) == 30;
  boolean testdaysFine2 = bk.fine(40) == 0;
  boolean testdaysFine3 = bk.fine(400) == 2000;
  boolean testdaysFine4 = cd.fine(48) == 400;
  boolean testdaysFine5 = cd.fine(30) == 0;
  boolean testdaysFine6 = cd.fine(300) == 2000;
  boolean testdaysFine7 = mw.fine(28) == 240;
  boolean testdaysFine8 = mw.fine(18) == 0;
  boolean testdaysFine9 = mw.fine(220) == 6000;
}

