import draw.*; /* --- CSU213 Spring 2005 Lecture Notes --------- Copyright 2005 Viera K. Proulx Lecture 8: Let me count the ways ... */ /* Goals: - learn to define and use methods for classes that represent lists - learn to define methods that produce lists We now learn to design methods that manipulate data represented by a self-referential classe hierarchy. We first focues on lists, and look at other self-referential data in the next lecture. Let us first define the classes and examples of instances of these classes. Our examples will involve a simple list of CDs, with the only information being the title and the number of tracks. Here are the class definitions and examples of data: */ /* +-----------------------+ | CD | +-----------------------+ | String title | | int tracks | +-----------------------+ | boolean same(CD that) | +-----------------------+ */ // to represent a CD class CD { String title; int tracks; CD(String title, int tracks) { this.title = title; this.tracks = tracks; } boolean same(CD that){ return this.title.equals(that.title) && this.tracks == that.tracks; } } /* +-------+ | ALoCD | +-------+ +-------+ / \ --- | ----------------- | | +--------+ +------------+ | MTLoCD | | ConsLoCD | +--------+ +------------+ +--------+ | CD first | | ALoCd rest | +------------+ */ // // // to represent a list of CDs // abstract class ALoCD { // /* // // ** DRAFT TEMPLATE ** Edit as needed. // // purpose statement // abstract ??? mmm(); // */ // } // // to represent an empty llist of CDs // class MTLoCD extends ALoCD { // // MTLoCD() { // } // // /* // // ** DRAFT TEMPLATE ** Edit as needed. // ??? mmm() { // } // */ // } // // to represent a non-empty list of CDs // class ConsLoCD extends ALoCD { // CD first; // ALoCd rest; // // ConsLoCD(CD first, ALoCd rest) { // this.first = first; // this.rest = rest; // } // // // ** DRAFT TEMPLATE ** Edit as needed. // ??? mmm() { // ... this.first ... // ... this.rest ... // } // */ // } // // /* // // ** DRAFT TEMPLATE ** Edit as needed. // ??? mmm() { // ... this.title ... // ... this.tracks ... // } // */ // } /* class Examples { Examples(){} CD help = new CD("Help", 12); CD pearl = new CD("Pearl", 8); CD tool = new CD("Tool", 13); CD boston = new CD("Boston", 10); ALoCD mtcd = new MTLoCD(); ALoCD list1 = new ConsLoCD(this.help, this.mtcd); ALoCD list2 = new ConsLoCD(this.pearl,this.list1); ALoCD list4 = new ConsLoCD(this.tool, new ConsLoCD(this.boston, this.list2)); } */ /* We are interested in solving the following problems: - compute the total number of tracks on all CDs in the list - find out if there are any long CDs in the list (with >10 tracks) - find out if all CDs are long ones - produce a list of all long CDs in the original list. 1. Problem analysis. Each of these methods should work equally well for empty list and non-empty lists as well. That means the method has to be declared as abstract in the class ALoCD, and a concrete variant has to be implemented in each of the derived classes. 2. Purpose and Header None of the methods consumes any additional data, and so we can write down the purpose and headers for all of them: // compute the total number of tracks on all CDs in this list abstract int totalTracks(); // determine whether there are CDs with >10 tracks in this list abstract boolean hasLongCD(); // determine whether all CDs in this list have more than 10 tracks abstract boolean allLong(); // produce a list of all long CDs (> t10 tracks) in this list abstract ALoCD longCDs(); 3. Examples. mtcd.totalTracks() --> 0 list1.totalTracks() --> 12 list2.totalTracks() --> 20 list3.totalTracks() --> 43 mtcd.hasLongCD() --> false list1.hasLongCD() --> true list2.hasLongCD() --> true list3.hasLongCD() --> true mtcd.allLong() --> true list1.allLong() --> true list2.allLong() --> false list3.allLong() --> false mtcd.longCDs() --> mtcd list1.longCDs() --> list1 list2.longCDs() --> mtcd list3.longCDs() --> new ConsLoCD(tool, new ConsLoCD(help, mtcd)) 4. Template. In the class MTLoCD there are no fileds, and so the template is empty. We need one basic template for all methods in the class ConsLoCD: ... this.first ... ... this.rest ... and add to it one entry for self-referential invocation of each method in this class: ... this.rest.totalTracks() ... ... this.rest.hasLongCD() ... ... this.rest.allLong() ... ... this.rest.longCDs() ... It is helpful at this time to read out loud the purpose statement for the self-referential method invocation: --- compute the total number of tracks on all CDs in the rest of this list --- determine whether there are CDs with >10 tracks in the rest of this list --- determine whether all CDs in the rest of this list have more than 10 tracks --- produce a list of all long CDs (> t10 tracks) in the rest of this list 5. Body. We first design methods for the MTLoCD class --- they follow from the examples: int totalTracks(){ return 0; } boolean hasLongCD(){ return false; } boolean allLong(){ return true; } ALoCD longCDs(){ return this; } Next we design the bodies for the methods in the ConsLoCD class: int totalTracks(){ return this.first.tracks + this.rest.totalTracks(); } boolean hasLongCD(){ return this.first.isLongCD() || this.rest.hasLongCD(); } boolean allLong(){ return this.first.isLongCD() && this.rest.allLong(); } ALoCD longCDs(){ if (this.first.isLongCD()) return new ConsLoCD(this.first, this.rest.longCDs()); else return this.rest.longCDs();} */ // The final code is: // to represent a list of CDs abstract class ALoCD { // compute the total number of tracks on all CDs in this list abstract int totalTracks(); // determine whether there are CDs with >10 tracks in this list abstract boolean hasLongCD(); // determine whether all CDs in this list have more than 10 tracks abstract boolean allLong(); // produce a list of all long CDs (> t10 tracks) in this list abstract ALoCD longCDs(); } // to represent an empty llist of CDs class MTLoCD extends ALoCD { MTLoCD() {} int totalTracks(){ return 0; } boolean hasLongCD(){ return false; } boolean allLong(){ return true; } ALoCD longCDs(){ return this; } } // to represnet a non-empty list of CDs class ConsLoCD extends ALoCD { CD first; ALoCD rest; ConsLoCD(CD first, ALoCD rest) { this.first = first; this.rest = rest; } int totalTracks(){ return this.first.tracks + this.rest.totalTracks(); } boolean hasLongCD(){ return this.first.tracks > 10 || this.rest.hasLongCD(); } boolean allLong(){ return this.first.tracks > 10 && this.rest.allLong(); } ALoCD longCDs(){ if (this.first.tracks > 10) return new ConsLoCD(this.first, this.rest.longCDs()); else return this.rest.longCDs();} } class Examples { Examples(){} CD help = new CD("Help", 12); CD pearl = new CD("Pearl", 8); CD tool = new CD("Tool", 13); CD boston = new CD("Boston", 10); ALoCD mtcd = new MTLoCD(); ALoCD list1 = new ConsLoCD(this.help, this.mtcd); ALoCD list2 = new ConsLoCD(this.pearl,this.list1); ALoCD list4 = new ConsLoCD(this.tool, new ConsLoCD(this.boston, this.list2)); // test the method totalTracks boolean testTotalTracks(){ return this.mtcd.totalTracks() == 0 && this.list1.totalTracks() == 12 && this.list2.totalTracks() == 20 && this.list4.totalTracks() == 43; } // test the method totalTracks boolean testHasLongCD(){ return this.mtcd.hasLongCD() == false && this.list1.hasLongCD() == true && this.list2.hasLongCD() == true && this.list4.hasLongCD() == true; } // test the method totalTracks boolean testAllLong(){ return this.mtcd.allLong() == true && this.list1.allLong() == true && this.list2.allLong() == false && this.list4.allLong() == false; } // test the method totalTracks /* --- we do not know how to compare two lists for equality - yet... see the next lecture --- boolean testLongCD(){ return this.mtcd.longCDs().same(mtcd) && this.list1.longCDs().same(list1) && this.list2.longCDs().same(list1) && this.list4.longCDs().same(new ConsLoCD(tool, new ConsLoCD(help, mtcd))); } */ boolean testAll(){ return this.testTotalTracks() && this.testHasLongCD() && this.testAllLong() // && this.testLongCD() ; } }