/* --- CSU213 Spring 2005 Lecture Notes --------- Copyright 2005 Viera K. Proulx Lecture 4: How many roads must a man walk down ... */ /* Goals: - recall self-referential data definitions from HtDP - translate data definitions into class diagrams and Java code - understand that the concepts are the same In previous lectures we talked about library items. However, we considered each item as a separate entity: one book, one CD, one magazine. However, a library needs to keep a list of all of its holdings - a catalog. Let's start by looking at a list of books. We have seen this last semester. The data definition was: A (List of Books) is one of - empty - (cons Book (List of Books)) with an arrow going back from the second item in the 'cons' back to the top. So we can think of cons as a structure with two components, two fields --- first and rest. From the last two lectures we know that if the data definition uses the phrase 'one of', we represent the data as an abstract class with one subclass for each of the variants. In this case, we would have two variants --- empty and cons. this leads to the following class diagram: +--------------+ | AListofBooks |<----------------+ +--------------+ | +--------------+ | / \ | --- | | | ------------------------ | | | | +---------------+ +-------------------+ | | MTListofBooks | | ConsListofBooks | | +---------------+ +-------------------+ | +---------------+ +-| Book first | | | | AListofBooks rest |----+ | +-------------------+ +--------+ | v +---------------+ | Book | +---------------+ | String title | | String author | | int no | +---------------+ This translates easily into Java code: */ // to represent a list of books abstract class AListofBooks { } // to represent an empty list of books class MTListofBooks extends AListofBooks { MTListofBooks() { } } // to represent a nonempty list of books class ConsListofBooks extends AListofBooks { Book first; AListofBooks rest; ConsListofBooks(Book first, AListofBooks rest) { this.first = first; this.rest = rest; } } /* Note, that the class that represents the empty list is cleary defined to be an empty list of books (not just any empty list). This is specified when we declare that MTListofBooks 'extends' AListofBooks. The constructor for this class does not need to initialize any fields, but still has to be present in our class definition: MTListofBooks() {} --- it expects no parameters () and takes no action {} to initialize fields. We need to make examples of lists of books, using some of the instances of books we have seen earlier: */ class ExamplesBooks{ ExamplesBooks() {} Book htdp = new Book("HtDP", 42, "Matthias"); Book aljafp = new Book("ALJaFP", 29, "Matthias"); Book ch = new Book("Cat in a Hat", 77, "Dr Seuss"); // Of course, we start with an empty list: AListofBooks mtlob = new MTListofBooks(); // Now we can make lists with several books: // a list of books with just one book AListofBooks list1 = new ConsListofBooks(this.htdp, this.mtlob); // a list with three books AListofBooks list3 = new ConsListofBooks(this.aljafp, new ConsListofBooks(new Book("1984", 22, "Orwell"), this.list1)); } /* Think back to what you have seen in HtDP and make more examples of lists of books. ----------------------------------------------------------------------------------*/ /* We return to thinking about library catalogs. The library has not only books, but also CDs, magazines, DVDs, maps, and other kinds of items. Any type of library item can be included in this list. Fortunately, we have already defined earlier an abstract class 'ALibItem' whose subclasses represent all kinds of library items. That allows us to talk about all of the library items as having the same type - ALibItem, and to make a list of them, using the same techinque as before: /* +----------+ | ACatalog |<---------------+ +----------+ | +----------+ | / \ | --- | | | -------------------- | | | | +-----------+ +----------------+ | | MTCatalog | | ConsCatalog | | +-----------+ +----------------+ | +-----------+ +-| ALibItem first | | | | ACatalog rest |----+ | +----------------+ | +----------+ | v +--------------+ | ALibItem | +--------------+ | String title | | int number | +--------------+ / \ --- | ----------------------------------------- | | | +---------------+ +---------------+ +-----------+ | Book | | CD | | Magazine | +---------------+ +---------------+ +-----------+ | String author | | String artist | | int year | +---------------+ | int tracks | | int issue | +---------------+ +-----------+ We can now define the classes in a straightforward way: */ // to represent a catalog of library items abstract class ACatalog { } // to represent an empty catalog of library items class MTCatalog extends ACatalog { MTCatalog() { } } // to represent a nonempty catalog of library items class ConsCatalog extends ACatalog { ALibItem first; ACatalog rest; ConsCatalog(ALibItem first, ACatalog rest) { this.first = first; this.rest = rest; } } // to represent a library item abstract class ALibItem { String title; int number; } // to represent a book in a library class Book extends ALibItem { String author; Book(String title, int number, String author) { this.title = title; this.number = number; this.author = author; } } // to represent a CD in a library class CD extends ALibItem { String artist; int tracks; CD(String title, int number, String artist, int tracks) { this.title = title; this.number = number; this.artist = artist; this.tracks = tracks; } } // to represent a magazine in a library class Magazine extends ALibItem { int year; int issue; Magazine(String title, int number, int year, int issue) { this.title = title; this.number = number; this.year = year; this.issue = issue; } } /* Of course, we need examples of all three kinds of library items, and of lists that contain all of these different items: */ class ExamplesCatalog { ExamplesCatalog() {} Book ch = new Book("Cat in a Hat", 77, "Dr Seuss"); Book htdp = new Book("HtDP", 42, "Matthias F"); CD thriller = new CD("Thriller", 99, "Michael Jackson", 10); CD pearl = new CD("Pearl", 88, "Janis Joplin", 12); Magazine time = new Magazine("Time", 52, 33, 2004); Magazine wired = new Magazine("Wired", 3, 44, 2004); // We can now make several lists: ACatalog mtcat = new MTCatalog(); ACatalog cat1 = new ConsCatalog(this.ch, this.mtcat); ACatalog cat2 = new ConsCatalog(this.pearl, new ConsCatalog(this.time, this.cat1)); ACatalog cat3 = new ConsCatalog(this.htdp, new ConsCatalog(this.thriller, this.cat2)); ACatalog cat4 = new ConsCatalog(new Book("ALJaFP", 29, "Matthias"), new ConsCatalog(this.wired, this.cat3)); } /* How many items are there in cat4? Run this program in ProfessorJ, and make more examples of library catalogs. ----------------------------------------------------------------------------------*/ /* Our next example concerns tidal streams of water going to the sea, that the fish that may swim through. At certain points the stream splits into two parts, continuing now as two different streams. Finally, a stream ends either in the open sea, or fizzles out, trapping the fish. The fish swims downstream, going down one of possible ways, until it either reaches a trap where it dies, or the open sea, where it lives happily ever after. We can describe the situation with the following draft of a data definition: ARiverPoint is one of: - Sea (where the river branch ends) - Trap (where the fish gets stuck and dies) - Split (where the fish can proceed in one of two directions) At each point there is some additional information, such as the physical location, (so we can draw this later on), the distance to this point from the previous one (the stream starts with some length going to the first split). We can record this information as an instance of some fictitious class "Info" that will be defined later. To get a better idea about the information available to us, we make a picture of a sample tidal stream, with letters s representing an open sea, letters t representing the end of a stream in a trap, and letter x marking the splitting points. | x1 / \ / \ / \ x2 x3 / \ / \ / \ s1 \ x4 t1 x5 / \ / \ t2 s2 s4 t3 This leads to the following data items: Sea s1 Sea s2 Sea s3 Sea s4 Trap t1 Trap t2 Trap t3 Split point x4: leading to t2 and s2 Split point x5 leading to s4 and t3 Split point x2 leading to x4 and t1 Split point x3 leading to s1 and x5 Split point x1 leading to x2 and x3 Now the data definitions begins to make sense and we are ready to draw the class diagram: +-------------+ | ARiverPoint |<-----------------------+ +-------------+ | +-------------+ | / \ | --- | | | --------------------------------------- | | | | | +-----------+ +-----------+ +-------------------+ | | Sea | | Trap | | Split | | +-----------+ +-----------+ +-------------------+ | | Info data | | Info data | | Info data | | +-----------+ +-----------+ | ARiverPoint left |----+ | ARiverPoint right |----+ +-------------------+ and the class definitions follow: */ // to represent a point on a tidal river abstract class ARiverPoint { } // to represent the sea end of a tidal river class Sea extends ARiverPoint { Info data; Sea(Info data) { this.data = data; } } // to represent a trap end of a tidal river class Trap extends ARiverPoint { Info data; Trap(Info data) { this.data = data; } } // to represent a spilt in a tidal river class Split extends ARiverPoint { Info data; ARiverPoint left; ARiverPoint right; Split(Info data, ARiverPoint left, ARiverPoint right) { this.data = data; this.left = left; this.right = right; } } /* We add a ficitious class Info, so we can make concrete examples: /* +-------------+ | Info | +-------------+ | String data | +-------------+ */ // to represent some information class Info { String data; Info(String data) { this.data = data; } } /* We now make examples of our river - placing the sample data in the class ExamplesRiver: */ class ExamplesRiver{ ExamplesRiver() {} ARiverPoint s1 = new Sea(new Info("s1-info")); ARiverPoint s2 = new Sea(new Info("s2-info")); ARiverPoint s3 = new Sea(new Info("s3-info")); ARiverPoint s4 = new Sea(new Info("s4-info")); ARiverPoint t1 = new Trap(new Info("t1-info")); ARiverPoint t2 = new Trap(new Info("t2-info")); ARiverPoint t3 = new Trap(new Info("t3-info")); ARiverPoint x4 = new Split(new Info("x4-info"), this.t2, this.s2); ARiverPoint x5 = new Split(new Info("x5-info"), this.s4, this.t3); ARiverPoint x2 = new Split(new Info("x2-info"), this.x4, this.t1); ARiverPoint x3 = new Split(new Info("x3-info"), this.s1, this.x5); ARiverPoint x1 = new Split(new Info("x1-info"), this.x2, this.x3); } /* Make additional examples of tidal rivers, to make sure you understand how this class hierarchy has been built. Compare it to the ancestor trees you have seen in HtDP. */