Lecture: 11 Date: Feb 4, 2008 Note card check --------------- Several former 211, 213 students have moved on to more advanced courses such as Pete Manolios' Logic and Computation course or Eli Barzilay's Programming Languages course (or Software Development, or Network Fundamentals, or ..., or ...). And obviously, after 211/213 you never need to rely on the Design Recipe again. You might as well forget it. We inflict it on you to gratify our own sadistic tendencies, which is after all, why we became professors in the first place. WRONG! We wouldn't waste our (or your) time teaching something you only need for a semester or even a year. You will rely on the Design Recipe over and over again, not only in tenure here at NEU, but in the rest of your career. But, I've been hearing word that some of these students have actually forgotten (or feigned ignorance of) the Design Recipe. So I am reinstituting an old Northeastern tradition, which will save you from debilitating embarrassment and ridicule further down the line. The Design Recipe Note Card. You are responsible for making a note card with the 6 steps of the Design Recipe: 1) Problem Analysis & Data Definition 2) Contract, Purpose & Effect Statements, Header 3) Examples 4) Function Template 5) Function Definition 6) Tests You must carry it with you AT ALL TIMES. I have given notice to Matthias Felleisen, the 213 Staff, and the rest of the NEU faculty that any student found without their note card will be SHAVED, STERILIZED, and DESTROYED! === We've introduced several new concepts over the past two lectures and some of them have been tangled together. I want to spend today untangling some of them, teasing out the distinctions and gaining some proficiency employing them. The concepts are: - Abstracting classes and lifting fields & methods - Subclasses, extending classes and overriding methods Let's return to our library item example: we want to represent Books and CDs and possibly other kinds of library items. interface ILibItem {} class Book { String title; String author; int catNo; Date due; ... } class CD { String title; String artist; int catNo; Date due; ... } Develop the methods a) isOverdue : is this library item overdue today? b) fine : compute the fine for this library item as of given date. Q: How do we compute (a) informally? A: Compare due date to given date. Overdue if due < given. Q: What information do we _need_ in order to compute this? A: This library items due date, and a given date. Q: What does this suggest about the header of isOverdue? A: boolean isOverdue(Date d) Q: What does this suggest about the development of isOverdue? A: Wishlist : in Date class, isBefore Write some examples. Date today = new Date(2008, 2, 4); ILibItem b1 = new Book("HtDP", "Felleisen", 898, new Date(2008, 2, 25)); ILibItem c1 = new CD("Colors", "K. Nordine", 771, new Date(2008, 2, 3)); b1.overDue(today) => false c1.overDue(today) => true Q: What information do we _have_ to compute this? (Template) In Book: boolean isOverdue(Date d) { /* Template ... all the fields and methods of Book ... ... this.due.isBefore(Date) ... boolean */ } Code it: boolean isOverdue(Date d) { return this.due.isBefore(d); } Q: How about in CD? A: Same. Let's do the fine method. Assume a book is charged $0.10 / day and a CD is charged $0.50 / day. Q: How do we compute this (informally)? A: Compute the number of days between this item's due date and the given date. Multiply this number by the daily fine rate for this item. Q: What does this suggest about the header? A: int fine(Date d) Q: What does this suggest about developing the code? A: Wishlist: in Date, int daysUntil(Date d) : compute the number of days from this date until the given date. In Book: int fine(Date d) return 10 * d.daysUntil(due); } In CD: int fine(Date d) return 50 * d.daysUntil(due); } ===================== Now we're going to put on our editor's hat: revise and condense, never say the same thing twice. Never Say The Same Thing Twice. Abstract and lift fields. 1) Identify the common fields and methods: * title, catNo, due * isOverdue, fine (?) 2) Define an _abstract_ super class, place lifted fields & methods. abstract class ALibItem { String title; int catNo; Date due; ... } 3) Rerun tests to make sure nothing was broken during abstraction. Every class has a different fine rate. We have design choices to make. 1) Put a field in the abstract class called rate, representing the rate in cents per day. The subclasses now give the appropriate rate when calling the super class. For example: In Book ------ Book(String title, String author, int catNo, Date due) { super(title, catNo, due, 10); // Note the rate argument. this.author = author; } Now we can move the fine method into the abstract class since it is always computed the same way: In ALibItem ----------- int fine(Date d) { return this.rate * this.due.daysUntil(d); } 2) We could leave it as is and define the method in each subclass, but since the methods will be nearly identical this is a bad idea. 3) If there were a common default case, say we had dozens of kinds of library items and they all charge $0.10 per day EXCEPT for CDs which charge $.50 per day, we could define the common case in the abstract class and override this method in the special case, ie. in CD.