// CS U213 Spring 2007 // Lecture 12: February 5, 2007 /* Goals: - Abstraction : Learning to design abstract classes and methods Introduction: Looking at the following data definition (represented as a class diagram) it is clear that there is a great deal of -- we hope unnecessary -- repetition. The design recipe for abstractions tells us to identify the commonalities and abstract over them. This may look like extra work, but sometimes it is actually necessary. +---------+ | LibItem |<----------------------------+ +---------+ | / \ | | | - - - - - - - - - - - - - - - - - - - - - - | | | | | +---------------+ +---------------+ +--------------+ | | Book | | CD | | Magazine | | +---------------+ +---------------+ +--------------+ | | int catNo | | int catNo | | int catNo | | | String title | | String title | | String title | | | int dueDate | | int dueDate | | int dueDate | | | String author | | String artist | | int vol | | +---------------+ +---------------+ | int no | | +--------------+ | | | +----------+ | | ListOfLI |<---------------+ | +----------+ | | +----------+ | | | | | / \ | | --- | | | | | --------------------- | | | | | | +------------+ +---------------+ | | | MTListOfLI | | ConsListOfLI | | | +------------+ +---------------+ | | +------------+ | LibItem first |----+-----------------------+ | ListOfLI rest |-+ | +---------------+ | | | | +--+ Consider the following problem. You are trying to find out if our list contains a given item. In order to do that, you need to be able to compare two library items and determine whether the catalog number of one of then is than the catalog number of the other one. This calls for the following purpose statement and method headers: IN ListOfLI: //does this list contain the given item? boolean contains(LibItem that); IN LibItem: //is this libItem same as the given item? boolean same(LibItem that); However, we have no instances of LibItems. So, we add the method to every class that implements LIbItem interface. In the class Book we look at the template: ... this.catNo ... -- int ... this.title ... -- String ... this.dueDate ... -- int ... this.author ... -- String and, of course, we have ... that ..., but the only thing we know about it is that it is a LibItem. We would like our method to handle the following cases: LibItem b1=new Book(77,"HtDP",2001,"MF") LibItem b2=new Book(33,"LPP",1944,"STX") LibItem c1=new CD(68,"Live",1992,"AC-DC") LibItem m1=new Magazine(39,"mw",10,3,2006) this.b1.same(this.b2)->false this.b1.same(this.b1)->true this.b1.same(this.c1)->false this.b1.same(this.m1)->false Our approach is problematic and thus we need to change our abstraction. ---------- ---------- |Solution| ---------- ---------- 1)Accessor-methods We could add to the interface LibItem "accessor-methods" headers that provide values of common fields // returns the catalog number of this item int getCatNo(); // returns the title of this item int getTitle(); // returns the due date of this item int getdueDate(); but this is not enough when the catalog number, the title and the due date are not enough to distinguish any two items!!!!! 2) The Visitor Pattern We add in the interface LibItem the following header methods //is this item the same as that item boolean same(LibItem that); //is this item the same as that Book boolean sameBook(Book that); //is this item the same as that CD boolean sameCD(CD that); //is this item the same as that Magazine boolean sameMagazine(Book that); and then we must implement all of this methods in every class. For example in class Book: //is this Book the same as that Book boolean sameBook(Book that){ ... }; //is this Book the same as that CD boolean sameCD(CD that){ return false}; //is this Book the same as that Magazine boolean sameMagazine(Book that){ return false};} //is this Book the same as that item boolean same(LibItem that){ return that.sameBook(this);} 3) ***Absraction*** But, we would like to indicate that the LIbItem classes have something more in common, namely fields --- and possibly some methods as well. To do this we use 'inheritance' - a common super class that is extended by several subclasses. We also say that the subclasses extend the super class. The super class can declare fields and methods, just like any other class. If all methods can be defined in the super class, it becomes a class just like any other. So, we can modify the class definitions as follows: +--------------+ | LibItem | +--------------+ | / \ | +----------------------------+ | ALibItem | +----------------------------+ | int catNo | | String title | | int dueDate | +----------------------------+ | boolean same(ALibItem that)| +----------------------------+ / \ --- | ---------------------------------------- | | | +---------------+ +---------------+ +----------+ | Book | | CD | | Magazine | +---------------+ +---------------+ +----------+ | String author | | String artist | | int vol | +---------------+ +---------------+ | int no | +----------+ We change the shape of the arrows - the inheritance arrow in the official UML is shown as a solid line with an open triangle at the end, as opposed to the implementation arrow that is made of dashed lines and an open arrow. The classes are now defined as follows: // to represent a library item abstract class ALibItem implements LibItem{ int catNo; String title; int dueDate; ALibItem implements LibItem(int catNo, String title){ this.catNo=catNo; this.title=title;} //is this item the same as that? abstract boolean same(ALibItem that}; } // to represent a book in a library class Book extends ALibItem { String author; Book(int catNo, String title, int dueDate, String author) { super(catNo, title, dueDate); this.author = author; } } // to represent a CD in a library class CD extends ALibItem { String artist; CD(int catNo, String title, int dueDate, String artist) { super(catNo, title, dueDate); this.artist = artist; } } // to represent a magazine in a library class Magazine extends ALibItem { int vol; int no; Magazine(int catNo, String title, int dueDate, int vol, int no) { super(catNo, title, dueDate); this.vol = vol; this.no = no; } } NOTE:The constructor in the classes Book, CD, and Magazine first calls the 'super' constructor and initializes the three common fields, then proceeds to initialize the remaining fields. The super constructor call --must be-- the first line in the body of the constructor in the subclass. We now continue to desing the method contains. We addd the following headers in the abstract class: //is this item the same as that? abstract boolean same(ALibItem that); //is this item the same as that Book abstract boolean sameBook(Book that); //is this item the same as that CD abstract boolean sameCD(CD that); //is this item the same as that Magazine abstract boolean sameMagazine(Book that); To indicate that there is no method body following the header, we declare the method to be 'abstract'. The net effect is similar to what we had seen with interfaces - it is a contract mandating that every class that extends this super class must either define this method, or declare it as abstract and be an abstract class. Any class that contains an abstract method must be abstract. And then we must override all of this methods in every class. For example in class Book: //is this Book the same as that Book boolean sameBook(Book that){ ... }; //is this Book the same as that CD boolean sameCD(CD that){ return false}; //is this Book the same as that Magazine boolean sameMagazine(Book that){ return false};} //is this Book the same as that item boolean same(LibItem that){ return that.sameBook(this);} Complete working code is included below. */ // to represent a library item interface LibItem{} abstract class ALibItem implements LibItem{ int catNo; String title; int dueDate; ALibItem(int catNo, String title, int dueDate){ this.catNo=catNo; this.title=title; this.dueDate=dueDate;} //is this item the same as that? abstract boolean same(ALibItem that); //is this item the same as that Book abstract boolean sameBook(Book that); //is this item the same as that CD abstract boolean sameCD(CD that); //is this item the same as that Magazine abstract boolean sameMagazine(Magazine that); } // to represent a book in a library class Book extends ALibItem { String author; Book(int catNo, String title, int dueDate, String author) { super(catNo, title, dueDate); this.author = author; } //is this Book the same as that Book boolean sameBook(Book that){ return this.catNo == that.catNo && this.title.compareTo(that.title)>=0 && this.dueDate == that.dueDate && this.author.compareTo(that.author)>=0; } //is this Book the same as that CD boolean sameCD(CD that){ return false;} //is this Book the same as that Magazine boolean sameMagazine(Magazine that){ return false;} //is this Book the same as that item boolean same(ALibItem that){ return that.sameBook(this);} } // to represent a CD in a library class CD extends ALibItem { String artist; CD(int catNo, String title, int dueDate, String artist) { super(catNo, title, dueDate); this.artist = artist; } //is this CD same as that Book boolean sameBook(Book that){ return false;} //is this CD the same as that CD boolean sameCD(CD that){ return this.catNo==that.catNo && this.title.compareTo(that.title)>=0 && this.dueDate==that.dueDate && this.artist.compareTo(that.artist)>=0; } //is this CD the same as that Magazine boolean sameMagazine(Magazine that){ return false;} //is this CD the same as that item boolean same(ALibItem that){ return that.sameCD(this);} } // to represent a magazine in a library class Magazine extends ALibItem { int vol; int no; Magazine(int catNo, String title, int dueDate, int vol, int no) { super(catNo, title, dueDate); this.vol = vol; this.no = no; } //is this Magazine the same as that Book boolean sameBook(Book that){ return false;} //is this Magazine the same as that CD boolean sameCD(CD that){ return false;} //is this Magazine the same as that Magazine boolean sameMagazine(Magazine that){ return this.catNo==that.catNo && this.title.compareTo(that.title)>=0 && this.dueDate==that.dueDate && this.vol==that.vol && this.no==that.no;} //is this Magazine the same as that item boolean same(ALibItem that){ return that.sameMagazine(this);} } class Examples{ ALibItem b1=new Book(77,"HtDP",2001,"MF"); ALibItem b2=new Book(33,"LPP",1944,"STX"); ALibItem c1=new CD(68,"Live",1992,"AC-DC"); ALibItem m1=new Magazine(39,"mw",10,3,2006); boolean test1 = check this.b1.same(this.b2) expect false; boolean test2 = check this.b1.same(this.b1) expect true; boolean test3 = check this.b1.same(this.c1) expect false; boolean test4 = check this.b1.same(this.m1) expect false; }