Visitors
5.3.5

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 Lab10-Visitors and add to it all files in from Lab10-Visitors.zip. You should have the following files:

Book.java

Song.java

Image.java

ILo.java

ISelect.java

ExamplesLists.java

NoSuchElementException.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.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.

We could now rewrite the solution to the circularly referential problem af books and authors (or actors with movie roles) using the generic list of objects and adding the appropriate visitor to the list of authors (or actors). Work this out on your own as a practice problem - it is not a required part.

You will finish this problem as a part of your next homework.