// CS U213 Spring 2007 // Lecture 7: January 24, 2007 /* Previously, no real new stuff, only 'dynamic dispatch' where java implements the 'cond' for us, we just have to put the cases in the right classes We've seen lists in Scheme. Now lets do an example, all in Java, for an iTunes-like application Start with a datatype to represent a single Song. Then we build a simple list of songs. Here's the class diagram: +----------------+ +---->| Song | | +----------------+ | | String title | | | String artist | | | int size | | | double price | | +----------------+ | | +--------+ | | LoS |<-------------------+ | +--------+ | | | | | / \ | | +---+ | | | | | +------+---------------+ | | | | | | +-----+-----+ | | | | MtLoS | +--------+-----+ | | +-----------+ | ConsLoS | | | +--------------+ | +------------------------| Song first | | | Los rest |--+ +--------------+ */ // Represents a Song for 'iTunes' class Song{ String title; String artist; int size; double price; Song(String title, String artist, int size, double price){ this.title = title; this.artist = artist; this.size = size; this.price = price; } } /* What things might one want to do/ask with an LoS? - How many songs are there? (ie. count them) - Find the total size? - Is there a song by artist X in the list? - Give me a list of all the songs by artist X in this list. (filter) One at a time... First we will show the implementation, then discuss each in turn */ // All Examples will be put in here... class Examples{ Examples(){} // * Examples, Songs and LoSs * Song help = new Song("Help", "Beatles", 50, 0.99); Song dsotm = new Song("Dsotm", "Pink Floyd", 100, 0.25); Song hts = new Song("Hts", "Muse", 10, 0.90); LoS mtlos = new MtLoS(); LoS list1 = new ConsLoS(this.help, this.mtlos); LoS list2 = new ConsLoS(this.dsotm, new ConsLoS( this.hts, this.list1)); // * Count Examples boolean countTests = ((check this.mtlos.count() expect 0) && (check this.list1.count() expect 1) && (check this.list2.count() expect 3)); // * Total Size Examples boolean totalSizeTests = ((check this.mtlos.totalSize() expect 0) && (check this.list1.totalSize() expect 50) && (check this.list2.totalSize() expect 160)); // * Contains Examples boolean containsTests = ((check this.mtlos.contains("Beatles") expect false) && (check this.list1.contains("Beatles") expect true) && (check this.list1.contains("Muse") expect false) && (check this.list2.contains("Muse") expect true) && (check this.list2.contains("ABC") expect false)); // * AllBy Examples boolean allByTests = ((check this.mtlos.allBy("Pink Floyd") expect this.mtlos) && (check this.list1.allBy("Pink Floyd") expect this.mtlos) && (check this.list1.allBy("Beatles") expect this.list1) && (check this.list2.allBy("Pink Floyd") expect new ConsLoS(this.dsotm, this.mtlos)) && (check this.list2.allBy("Beatles") expect this.list1)); } // Represents a List of Songs... interface LoS{ // Count the number of songs in *this* LoS int count(); // Find the total size of all the songs in *this* LoS int totalSize(); // Tell if there is a song by the given artist in *this* LoS boolean contains(String artist); // Create a list of all songs by the given artist in *this* LoS LoS allBy(String artist); } // Represents an empty List of Songs... class MtLoS implements LoS{ // Nothing to be done for MtLoS MtLoS(){} // Count the number of songs in *this* empty LoS int count(){ return 0; } // Find the total size of all the songs in *this* empty LoS int totalSize(){ return 0; } // Tell if there is a song by the given artist in *this* empty LoS boolean contains(String artist){ return false; } // Create a list of all songs by the given artist in *this* empty LoS LoS allBy(String artist){ return this; } } // Represents a non-empty List of Songs... class ConsLoS implements LoS{ Song first; LoS rest; ConsLoS(Song first, LoS rest){ this.first = first; this.rest = rest; } // ... this.first ... --- Song // ... this.first.title ... --- String // ... this.first.artist ... --- String // ... this.first.size ... --- int // ... this.first.price ... --- double // ... this.rest ... --- LoS // Count the number of songs in *this* non-empty LoS int count(){ return 1 + this.rest.count(); } // Find the total size of all the songs in *this* non-empty LoS int totalSize(){ return this.first.size + this.rest.totalSize(); } // Tell if there is a song by the given artist in *this* non-empty LoS boolean contains(String artist){ return this.first.artist.equals(artist) || this.rest.contains(artist); } // Create a list of all songs by the given artist in *this* non-empty LoS LoS allBy(String artist){ if(this.first.artist.equals(artist)) return new ConsLoS(this.first, this.rest.allBy(artist)); else return this.rest.allBy(artist); } } /* ****************************************** How Many... * Problem analysis/data definition; Above * Purpose/Contract Purpose : to count the songs in *this* ListOfSongs Contract: int count(); * Template: we update after the ConsLoS constructor to include our new method, 'count' // ... this.rest.count() ... --- int * Implementation... we update each of the definitions to include the new method... ****************************************** Total Size... * Purpose/Contract Purpose : to find the total size of all the songs in *this* List of Songs Contract: int totalSize(); * Examples... added to the Examples class * Template: we update afterthe ConsLoS constructor to include our new method, 'totalSize' // ... this.rest.totalSize() ... --- int * Implementation... we update each of the definitions to include the new method... ****************************************** Note that we have to use our knowledge of the inner details of a Song. Using (or not using) direct access to dats-members is mostly a judgment call. In this case ConsLoS and Song are reasonably good 'friends' but this is mainly to save board space. Normally we would create functions to access the members of Song. For instance... imagine that instead of storing the 'size' directly, it made sense to store the length (in seconds) and the encoding type. We would then give a method to find the size of the Song using this information instead of allowing direct access. If we used a method instead of the field directly both implementations would work with our ConsLoS without changing any of the ConsLoS code. ****************************************** Countains... * Purpose/Contract Purpose : Tell if there is a song by the given artist in *this* List of Songs Contract: boolean contains(String artist); * Examples... added to the Examples class * Template: we update after the ConsLoS constructor to include our new method, 'contains' // ... this.rest.contains(String artist) ... --- boolean * Implementation... we update each of the definitions to include the new method... ****************************************** All Songs By Artist... * Purpose/Contract Purpose : Create a list of all songs by the given artist in *this* List of Songs Contract: LoS allBy(String artist); * Examples... added to the Examples class * Template: we update after the ConsLoS constructor to include our new method, 'allBy' // ... this.rest.allBy(String artist) ... --- LoS * Implementation... we update each of the definitions to include the new method... */