Week 09: Lab 5
Objectives
To explore small class hierarchies and polymorphism.
Exercise One
Building on the homework 4 exercise:
-
Extend (and modify as needed) your Student class from Lab 4 to create a ResearchStudent subclass. This class will need to have an additional field which is the name of the supervisor. Create a
printInfo()
method to print out the students’ fields and, in the case of the research student, their supervisor. -
Write StudentTest class. Create an ArrayList of your Student and ResearchStudent classes. Add a mix of students and research students, then loop the ArrayList calling your
printInfo()
method. -
Using the
getClass()
method, loop through ArrayList and print out whether the class is a Student or a ResearchStudent. -
Repeat the exercise above, this time using the
instanceof
operator.
Exercise Two
Change Student into an abstract class by removing implementation of
printInfo()
method. Subclass it into Undegraduate and ResearchStudent
accordingly. Modify the StudentTest class to work with Student type as
elements of ArrayList. Make the code to print the list of students from the
ArrayList without using the getClass()
or instanceof
but relying
on polymorphism instead. Now the code is much simpler — examine it, convince
yourself and learn to appreciate.
Further programming ideas: Visitor pattern
Imaging that your type hierarchy is large and deep — let’s not talk about
students, but about employees in a gigantic organisation; very many different
types of employee, all have the same number of overridden methods, declared in
the abstract class Employee: printInfo()
, calculateSalary()
,
paySupaContribution()
, calculatePaidLeave()
etc. There are Clerks,
Managers, GeneralStaff, SeniorManagers, CEOs, Cleaners and so on.
They all have different employment conditions, and different implementation of
the above methods. Imagining also that the number of operations (methods) which
we would like all our types to be able to perform is changeable, it can grow.
To implement newly added methods in each and every subclass of Employee can
quickly become quite a task.
There is a better way: to remove all methods from Employee and its subclasses and replace them with just one:
public abstract void accept(Visitor visitor);
Here, Visitor is a new type represented by an interface and a hierarchy of classes which implement it. The Visitor declares all the methods which were removed from the Employee hierarchy:
public void visit(Clerk c);
public void visit(Manager m);
public void visit(Cleaner c);
public void visit(GeneralStaff gs);
....
and the classes implementing Visitor, provide implementation bodies for all visit-methods:
class SalaryCalculator implements Visitor {
private double salary;
public void visit(Clerk c) {
salary = c.getSalary();
}
public void visit(Manager m) {
salary = c.getSalary() + c.getBonus();
}
......
public double calculate() { return salary; }
}
and similarly for all other Visitor classes. The Employee hierarchy classes
all have the same implementation of the accept
method:
public void accept(Visitor v) {
v.visit(this);
}
When a client has a number of Employee objects and wants to call the same operation of them (to calculate total salary part of the budget, or print the organization roll, it would simply visit each employee with the salary calculator as follows:
SalaryCalculator sc = new SalaryCalculator();
employee.accept(sc);
total += sc.calculate();
The “invited” visitor will come to the caller object (this
reference!) and
do what was asked.
The visitor pattern is particularly effective when the visited (Employee here) hierarchy types are defined recursively (expression trees).
Confused? Intrigued? Learn more about the Visitor pattern (search the web), and try to use in the above “Student” (or “Employee”) examples. Do some more research and find out why the Visitor Pattern is such a double-edged sword — discover problems which its use may create that turn advantages into liabilities.