Lecture: Methods for simple classes
Design methods for simple classes of data.
Design methods for classes that contain objects of another class.
Wish lists.
Book.java
BookAuthor.java
Book2.java
Lecture outline
Methods for simple classes: The design recipe
Arithmetic expressions in Java
Methods for classes with containment: Wish lists
Methods that produce objects
Designing methods for simple classes
This code is in the file Book.java
We want to represent an book with an author as data in DrRacket and then in Java.
Here is the data definition in DrRacket, as well as examples of data:
;; to represent a book in a bookstore 
;; A Book is (makebook String String Number) 
(definestruct book (title author price)) 

;; Examples: 
(define htdp (makebook "HtDP" "FFFK" 60)) 
(define beaches (makebook "Beaches" "PC" 20)) 
We now want to define a function that will produce the book price on the day we have a sale. On the sale day, all books are discounted by the same percentage. Following the Design Recipe we produce the following function and tests:
;; compute the sale price of a book for the given discount 
;; saleprice: Book Num > Num 
(define (saleprice abook disc) 
( (bookprice abook) (/ (* (bookprice abook) disc) 100))) 

(checkexpect (saleprice htdp 30) 42) 
(checkexpect (saleprice beaches 20) 16) 
Note that this function is useless for other types of data  they require that we provide a book.
In Java all computations relevant for one type of data are defined as methods in the class that represents the relevant data. (We say that the methods represent the behavior of the objects that are the members of this class.)
We start by rewriting the above data definitions as a class diagram and defining the classes that represent books and examples of data and computation relavent for books:
The class diagram and the Java class definitions for the class of books is straightforward:
++ 
 Book  
++ 
 String title  
 String author  
 int price  
++ 
To define the method, we follow the same Design Recipe as before. Rather than tracing the steps of the Design Recipe we show the complete program together with the test suite and add comments that explain how the whole program has been built.
The class definition starts with the constructor, the template is written once for the whole class and comes right after the constructor (because it grows as more methods are added to the class definition, and always involves all fields defined in the class). The individual method definitions come after that.
Every method definition consists of the following parts:
purpose statement  same as before, except for the use of the word this
the method name  by convention starts with a lower case letter
the argument list where the type of every argument is explicitly specified
the method body  the code to execute when the method is invoked
Here is the Java code:
// to represent a book in a bookstore 
class Book { 
String title; 
String author; 
int price; 

// the constructor 
Book(String title, String author, int price) { 
this.title = title; 
this.author = author; 
this.price = price; 
} 
} 

/* TEMPLATE: 
... this.title ...  String 
... this.author ...  String 
... this.price ...  int 

Note that the template includes all fields defined in this class 
and specifies their types as well. We will extend the template 
in the subsequent examples. 
*/ 

/* The method definition comes here. */ 
We start with a comment, a purpose statement, then write the contract and the header. Here the contract and the header are combined into one line that specifies the name of the method, the type of value that the method will produce, and includes not only the list of argument names, but for each it specifies its data type as well.
There is no argument of the type Book included in the list of arguments  because only objects of the type Book are allowed to ask what is their sale price. We say that an instance of the Book class (or object of the type Book ) invokes the methods defined in the Book class. This object acts as an implicit argument for the method. Within the body of the method we refer to the object that invoked the method by the keyword this, and can access individual fields for this book as this.title or this.price, etc. (These are the selectors, simlar to what in DrRacket was written as (booktitle abook) or bookprice abook)
The examples are shown below and show that each method evaluation request is preceeded by the book object for which the computation should be made.
The helps us in building the template – shown above. All methods defined in the Book class will have the same basic template  and so we write the template as a block comment right after the constructor for this class, before all method definitions.
Back to the actual code: here is the method definition:
// compute the sale price of this book given today's discount 
// in percent of the priginal price 
int salePrice(int discount) { 
return this.price  (this.price * discount) / 100; 
} 
The Examples... class contains both the data definitions for our class and the tests for the method we just defined (of course, we only can test a method if we have the needed data).
// examples and tests for the class hierarchy that represents 
// books and authors 
class ExamplesBooks { 
ExamplesBooks() {} 

// examples of boooks 
Book htdp = new Book("HtDP", "FFK", 60); 
Book beaches = new Book("Beaches", "PC", 20); 

/* The tests are defined as methods that produce a boolean value 
that indicates whether the tests passed. We need to add an 
'import' statement on the top of the file that indicates that 
we will use the tester library. The library evaluates the 
checkExpect methods that consume two values and produces 
true or false depending on whether the two values are the same. 
*/ 

// test the method salePrice for the class Book 
boolean testSalePrice(Tester t) { 
return 
t.checkExpect(this.htdp.salePrice(30), 42) && 
t.checkExpect(this.beaches.salePrice(20), 16); 
} 

/* We can now run our program and see the test results. */ 
} 
Evaluation of arithmetic expressions
The method body contained the following formula:
this.price  (this.price * discount) / 100 
Well, for similar expressions in DrRacket we knew exactly how they will be evaluated. Every function invocation was enclosed in parentheses, with the first item after the opening left parentheses representing the function to evaluate, with the remaining items prior to the closing right parentheses representing the arguments for the function. And the evaluation started with the innermost set of parentheses.
In Java, the arithmetic operators try to mimic the conventions used in mathematical formulas by assigning the order of preference to the operations. The multiplication and addition takes precedence over addition or subtraction and so this expression is evaluated as if the parentheses were given this way:
(this.price  ((this.price * discount) / 100)) 
Additonally, operations at the same lever of preference are evaluated lefttoright, and os this expression can also be written as:
this.price  this.price * discount / 100 
Smart programmer adds extra parentheses whenever the order of operations is not clear, or to emphasise the meaning of the formula.
Methods for classes with containment: Designing method templates
This code is in the file BookAuthor.java
Let us continue with the example that included a book and the author:
// to represent a book in a bookstore 
class Book { 
String title; 
Author author; 
int price; 

// the constructor 
Book(String title, Author author, int price) { 
this.title = title; 
this.author = author; 
this.price = price; 
} 
} 

// to represent a author of a book in a bookstore 
class Author { 
String name; 
int yob; 

// the constructor 
Author(String name, int yob) { 
this.name = name; 
this.yob = yob; 
} 
} 


// examples and tests for the class hierarchy that represents 
// books and authors 
class ExamplesBooks { 
ExamplesBooks() {} 

// sample author and a book 
Author pat = new Author("Pat Conroy", 1948); 
Book beaches = new Book("Beaches", this.pat, 20); 
} 
We would like to know if the authors of two books are the same. We follow the design recipe in designing this method. In DrRacket the function would have the purpose and header as follows:
;; are the two given books by the same author? 
;; sameauthor? : Book Book > Boolean 
(define (sameauthor book1 book2)...) 
In Java, the first book becomes the implicit argument, the instance which invokes the method, and the second book will be the sole argument for the method. Here is the purpose and the header:
// is this book written by the same author as the given book? 
boolean sameAuthor(Book that) {...} 
We need examples:
// examples of authors 
Author pat = new Author("Pat Conroy", 1948); 
Author dan = new Author("Dan Brown", 1962); 

// examples of books 
Book beaches = new Book("Beaches", this.pat, 20); 
Book prince = new Book("Prince of Tides", this.pat, 15); 
Book code = new Book("Da Vinci Code", this.dan, 20); 

// test the method sameAuthor in the class Book 
boolean testSameBookAuthor(Tester t) { 
return 
t.checkExpect(this.beaches.sameAuthor(this.prince), true) && 
t.checkexpect(this.beaches.sameAuthor(this.code), false); 
} 
Now we look at the template. The common template for all methods in the class Book looks like this:
/* TEMPLATE: 
Fields: 
... this.title ...  String 
... this.author ...  Author 
... this.price ...  int 

Methods: 
... this.salePrice(int) ...  int 
... this.sameAuthor(Book) ...  boolean 

Methods for fields: 
... this.author.mmm(??) ...  ?? 
*/ 
We first add all methods defined for the Book class. That includes this.salePrice(int), defined earlier, as well as this.sameAuthor(Book) we are defining now. So, for example, our method this.sameAuthor could invoke this.salePrice  though for this method it is useless.
If one of the fields in the class is an instance of another class,
we add to the template all methods defined for that class, as they
can be invoked by that field. Here we would add any methods
defined for the Author class —
Just as in DrRacket, we then add any information we can extract from the parameters of the method, in this case, any information that that book can provide. It could invoke the methods in its defining class, and, if the argument is of the same type as the class where the method is defined, we can also access all of its fields.
So, our template for this method expands to:
/* TEMPLATE: 
Fields: 
... this.title ...  String 
... this.author ...  Author 
... this.price ...  int 

Fields for the method parameters: 
... that.title ...  String 
... that.author ...  Author 
... that.price ...  int 

Methods: 
... this.salePrice(int) ...  int 
... this.sameAuthor(Book) ...  boolean 

Methods invoked by the method parameters: 
... that.salePrice(int) ...  int 
... that.sameAuthor(Book) ...  boolean 

Methods for fields: 
... this.author.mmm(??) ...  ?? 
... that.author.mmm(??) ...  ?? 
*/ 
Trying to finish the design of the method we see that we do not have enough information available. We need to know whether the authors are the same  but only the Author class can define the method that would compare two authors.
So, we make a wish list: we need to define the method sameAuthor in the Author class:
// is this the same author as the given one? 
boolean sameAuthor(Author that) {...} 
We can now add this method to the template:
Methods for fields: 
... this.author.sameAuthor(Author) ...  boolean 
... that.author.sameAuthor(Author) ...  boolean 
and the method body becomes:
// is this book written by the same author as the given book? 
boolean sameAuthor(Book that) { 
return this.author.sameAuthor(that.author); 
} 
Of course, before we test this method, we need to finish designing the method in our wish list:
// to represent a author of a book in a bookstore 
class Author { 
String name; 
int yob; 

// the constructor 
Author(String name, int yob) { 
this.name = name; 
this.yob = yob; 
} 

/* TEMPLATE 
Fields: 
... this.name ...  String 
... this.yob ...  int 

Methods: 
... this.sameAuthor(Author) ...  boolean 
*/ 

// is this the same author as the given one? 
boolean sameAuthor(Author that) { 
return this.name.equals(that.name) && 
this.yob == that.yob; 
} 
} 
with the added tests:
// test the method sameAuthor in the class Author 
boolean testSameAuthor(Tester t) { 
return 
t.checkExpect( 
this.pat.sameAuthor(new Author("Pat Conroy", 1948)), 
true) && 
t.checkexpect(this.pat.sameAuthor(this.dan), false); 
} 
Methods that produce objects
This code is in the file Book2.java
The methods so far produced results of the primitive type. We now look at how to design methods that produce objects.
Suppose the bookstore want to permanently decrease the price of all books by 20%. We need a method that produces a book with the price redced as desired.
The purpose and header will be:
// produce a book like this one, but with the price reduced by 20% 
Book reducePrice(){...} 
Here are some examples:
// examples of books 
Book htdp = new Book("HtDP", "FFK", 60); 
Book beaches = new Book("Beaches", "PC", 20); 

// test the method reducePrice for the class Book 
boolean testReducePrice(Tester t) { 
return 
t.checkExpect(this.htdp.reducePrice(), 
new Book("HtDP", "FFK", 48)) && 
t.checkExpect(this.beaches.reducePrice(), 
new Book("Beaches", "PC", 16)); 
} 
We see that the result is a new object and so the method body will contain return new Book(...). The template is as follows:
/* TEMPLATE: 
Fields: 
... this.title ...  String 
... this.author ...  String 
... this.price ...  int 

Methods: 
... this.salePrice(int) ...  int 
... this.reducePrice() ...  Book 
*/ 
We see that we can use the method defined earlier in the body of our method as follows:
// produce a book like this one, but with the price reduced by 20% 
Book reducePrice() { 
return new Book(this.title, this.author, this.salePrice(20)); 
} 
Of course, we finish by running the tests. Notice that the test cases now compare the values of two objects, not just the values of data of the primitive types.