Lab 10a
Goals: The goals of this lab is to learn how to design and use visitor for collection of classes in a class hierarchy.
Visitors
We often do not know what kinds of methods the programmers need to define for their lists of objects. Furthermore, they may have to define one specific method for their list that is not applicable to any other list of objects.
For example, we may want to use these classes for our circularly defined data. So we may want to add the method addBook to the classes that represent the list of authors (or addRole to the classes that represent the list of movie actors).
To make the library extensible, the designers need to provide ways for adding new methods at the time the programmer wants to use it.
One way to make the self-referential union of classes extensible is by defining a visitor interface and adding to the union a method that invokes the methods of the visitor interface supplying its own internal data as arguments. This sounds very complicated. We will illustrate this on an example.
Start with a new project Lab10a-Visitors and add to it all files in from Lab10a-Visitors.zip. You should have the following files:
Book.java
Song.java
Image.java
ILo.java
ExamplesLists.java
ILoVisitor.java
Here is an example of a visitor interface for the classes that define a list of objects:
// A visitor for the ILo<T> classes that |
// consumes no arguments |
// and produces the result of the type R |
interface ILoVisitor<R, T>{ |
// method for the empty list |
public R forMt(); |
|
// method for the nonempty list |
public R forCons(T first, ILo rest); |
} |
The visitor for a union of data types contains one method for every class in the union, and has the name that indicates the class it targets. The arguments for each method are the fields in the target class that are needed to perform any operations on an object in this field. So, here, the method forMt takes no arguments, as there are no relevant fields in the class MtLo<T>. On the other hand, the method forCons consumes two arguments: first and rest, where the first is of the type T and the rest is of the type ConsLo<T>.
We now add to the classes MtLo<T> and ConsLo<T> the hooks, the methods that accept the instance of the visitor class and invoke the appropriate methods defined there:
// in the interface ILo<T>: |
// accept the visitor that produces a result of the type R |
public <R> R accept(ILoVisitor<R, T> ilov); |
|
// in the class MtLo< T>: |
// accept the visitor that produces a result of the type R |
public <R> R accept(ILoVisitor<R, T> ilov){ |
return ilov.forMtLo(); |
} |
|
// in the class MtLo< T>: |
// accept the visitor that produces a result of the type R |
public <R> R accept(ILoVisitor<R, T> ilov){ |
return ilov.forConsLo(this.first, this.rest); |
} |
The example included in the Lab10a-Visitors.zip files defines a visitor that represents a method that computes the total download time for all files in the list of image files.
The visitor class (that implements the ILoVisitor<T> interface is defined as follows:
// A visitor that computes the total download time for all files |
// in the list of image files |
class ILoImageDownloadTimeVisitor |
implements ILoVisitor<Integer, Image>{ |
|
int speed; |
ILoImageDownloadTimeVisitor(int speed){ |
this.speed = speed; |
} |
|
// method for the empty list |
public Integer forMt(){ |
return 0; |
} |
|
// method for the nonempty list |
public Integer forCons(Image first, ILo rest){ |
return |
first.fileSize / speed + rest.accept(this); |
} |
} |
The following examples show how we can use this method with our lists of data:
ILoVisitor imageDownloads = |
new ILoImageDownloadTimeVisitor(200); |
|
// test the use of the ILoMakeStringVisitor |
void testILoMakeStringVisitor(Tester t){ |
t.checkExpect(this.mtloi.accept(this.imageDownloads), 0); |
t.checkExpect(this.ilist2.accept(this.imageDownloads), 287); |
} |
We see that the methods defined in the class that implements the visitor have the same structure as those originally defined inside of the MtLo... and ConsLo... classes. The instance of the list then invokes the new method by applying the accept method with the appropriate visitor as its argument.
Make another example of the use of the ILoImageDownloadTimeVisitor that computes the download time with the download speed 100 for the list ilist3.
Design a visitor that implements the method that produces a list of titles of all songs in a list of songs. Of course, you need to make sure that your solution works.
Design a visitor class that implements the method that produces one long String that contains the titles of all books, separating the titles with the new line String that is encoded as "\n". Of course, you need to make sure that your solution works.
Finish this problem as a part of your next homework.