15.20 A JAVA STUDY OF A SMALL CLASS HIERARCHY EXHIBITING MODERATELY COMPLEX BEHAVIOR


15.20 A JAVA STUDY OF A SMALL CLASS HIERARCHY EXHIBITING MODERATELY COMPLEX BEHAVIOR

While programming a Java class hierarchy is much easier than programming a similar C++ class hierarchy, one still has to make design decisions regarding what containers to use, how to take advantage of polymorphism to bring about a certain behavior, when to copy objects through cloning and when to be content with just passing object references around, and so on.

While, relative to C++, you now don't have to worry about explicit memory allocation and deallocation, the writing of copy constructors and copy assignment operators, the writing of destructors, and so on, it does not mean that you can throw caution to the wind when programming in Java. You still have to worry about making sure that you are not trying to access a null reference, that you are not passing the same object reference around in a program while your intent was to create duplicates of an object since you did not want changes in one section of the program to affect program behavior elsewhere, and so on. So you have to make conscious program design decisions about when to clone and when not to clone, and so on.

click to expand
Figure 15.14

We will use the classes of Figure 15.14 for the code in this section.[19] We want this twin hierarchy of classes to exhibit the same behavior as before. While an Employee can have any dog, a Manager can only have Poodles and Chihuahuas. So if a Manager constructor is presented with a container full of dogs, it should only select Poodles and Chihuahuas from the container. For the C++ case, we used RTTI for this check in the Manager constructor in line (O) of Manager.cc. (Also see the lines labeled (F) through (I) of the code in the previous section and the associated explanations.)

The question now is, how can the same sort of runtime type checking be carried out in Java? In Java, not knowing whether a reference stored in the list dogs is to a Poodle object or to a Chihuahua object, or to neither, we can go ahead and try to cast it to one of these objects. If the cast is not valid, a ClassCastException will be thrown. So catching of the exception can be used to figure out whether or not a Dog reference is a Poodle or a Chihuahua reference, as in the loop below:

      //dogs is a list of all dogs presented to Manager constructor      ListIterator iter = dogs.listIterator();      while ( iter.hasNext () {          Object object = iter.next ();          try {              Poodle p = (Poodle) object;                        //(A)              addDogToDogs ( (Dog) p.clone () );                 //(B)          } catch ( ClassCastException badpoodlecast ) {         //(C)              try {                  Chihuahua c = (Chihuahua) object;              //(D)                  addDogToDogs ( (Dog) c.clone() );              //(E)              } catch( ClassCastException badhuahuacast ) {}     //(F)          }      } 

where the lines (A) and (C) help us figure out at runtime whether a Dog reference is actually a Poodle reference, and the lines (D) and (F) do the same vis-à-vis a Chihuahua reference. The calls to addDogToDogs in lines (B) and (E) add a new dog to those already assigned to a Manager object. Following the same implementation design as in the previous section, a part of the contract of this method is to make sure that the dogs always stay sorted according to their weight, which takes us to the issue discussed next. The runtime type checking shown above is incorporated in the Manager constructor in line (O) of the program below.

As was the case in the previous section, besides runtime type checking, what makes the current study moderately complex is that the Poodles and Chihuahuas that are owned by a Manager are to be stored in a sorted order, the sorting based on an attribute —weight—that is not defined for the parent class Dog, but only for the derived classes Poodle and Chihuahua (see lines (K) and (M) of the program below). The sorted list is to be kept in the Employee slice of a Manager object.

If the sorted list of Dogs for a Manager object is to be kept in the Employee slice of the Manager, we need to provide the Employee class with a function that can add new dogs to the list and make sure that function is available in the Manager class. And if this list of Dogs is to be sorted by using a parameter that exists only for Poodles and Chihuahuas, we need to define for Dog a method that would return the appropriate sorting criterion for each subclass of Dog. That way, when we are sorting a list of Dogs (that in actuality are Poodles and Chihuahuas), we can use polymorphism to scoop up the correct sorting parameter. In the code shown below, line (G) provides the base class Dog with the following method with a trivial implementation

      public double getDogCompareParameter(){ return O; } 

and then, in lines (L) and (N), we supply the derived classes Poodle and Chihuahua with the following override definition:

      public double getDogCompareParameter(){ return weight; } 

This method is called in the compare method of the Dog_Comparator class in line (H) of the program below:

      public static class Dog_Comparator implements Comparator {          public int compare( Object o1, Object o2 ) {              Dog d1 = (Dog) o1;              Dog d2 = (Dog) o2;              if ( d1.getDogCompareParameter()                                == d2.getDogCompareParameter() )                  return O;              return ( d1.getDogCompareParameter()                          < d2.getDogCompareParameter() ) ? -1 : 1;          }      } 

The Dog_Comparator class is an inner class of Dog.[20] A Dog_Comparator object is supplied to the Collections.sort routine in the following definition of addDogToDogs method of the Employee class:

      public void addDogToDogs ( Dog newDog ) {          if ( dogs == null ) dogs = new ArrayList() );          dogs.add( newDog );          Collections.sort( dogs, new Dog.Dog_Comparator() );      } 

This method is provided to the Employee class in line (I) of the code shown below.

The program shown below is an extension of the Java case study presented in Section 11.16 of Chapter 11. The program starts out by presenting the same classes as in the earlier Java study. This part of the code consists of the main class Employee and the ancillary classes Date, Dog, Cat, and Auto. The ancillary classes are used for the different data members in the main class Employee. Subsequently, the program defines the derived ancillary classes Poodle and Chihuahua and the derived main class Manager.

 
//Manager.Java import java.util.*; // for Comparator, Collections, etc //////////////////////////// class Date ///////////////////////////// class Date implements Cloneable { private int month; private int day; private int year; public Date( int mm, int dd, int yy ) { month = mm; day = dd; year = yy; } public String toString() { return month + " : " + day + " : " + year; } public Object clone() { Date date = null; try { date = ( Date ) super.clone(); } catch( CloneNotSupportedException e ) {} return date; } } ///////////////////////////// class Cat /////////////////////////////////// class Cat implements Cloneable { private String name; private int age; public Cat( String nam, int a ) { name = nam ; age = a; } public String toString() { return " Name: " + name + " Age: " + age; } public Object clone() { Cat cat = null; try { cat = ( Cat ) super.clone(); } catch( CloneNotSupportedException e ) {} return cat; } } ///////////////////////////// class Dog ///////////////////////////// class Dog implements Cloneable { private String name; private int age; public Dog( String nam, int a ) { name = nam; age = a; } public String toString() { return "\nName: " + name + " Age: " + age; } public String getName() { return name; } public int getAge() { return age; } public void print() { System.out.println( this ); } public Object clone() { Dog dog = null; try { dog = (Dog) super.clone(); } catch ( CloneNotSupportedException e ) {} return dog; } public double getDogCompareParameter(){ return O; } //(G) public static class Dog_Comparator implements Comparator { //(H) public int compare ( Object o1, Object o2) { Dog d1 = (Dog) o1; Dog d2 = (Dog) o2; if ( d1.getDogCompareParameter () == d2.getDogCompareParameter() ) return 0; return ( d1.getDogCompareParameter () < d2.getDogCompareParameter() ) ? -1 : 1; } } } /////////////////////////// class Employee ////////////////////////// class Employee { // intentionally left uncloneable String firstName, lastName; Date dateOfBirth; Employee[] friends; Auto[] autos; Cat kitty; ArrayList dogs; Map phoneList; public Employee ( String first, String last ) { firstName = first; lastName = last; } public Employee ( String first, String last, Date dob ) { firstName = first; lastName = last; dateOfBirth = dob == null ? null : (Date) dob.clone(); } public Employee ( String first, String last, Date dob, Cat kit ) { firstName = first; lastName = last; dateOfBirth = dob == null ? null : (Date) dob. clone(); kitty = kit == null ? null : (Cat) kit.clone(); } public Employee ( String first, String last, ArrayList dogs ) { firstName = first; lastName = last; this.dogs = dogs == null ? null : (ArrayList) dogs.clone(); } Employee ( String first, String last, Date dob, Employee[] fnds ) { firstName = first; lastName = last; dateOfBirth = dob == null ? null : (Date) dob.clone(); friends = fnds == null ? null : (Employee[]) fnds.clone(); } Employee( String first, String last, Map phoneList ) { firstName = first; lastName = last; this.phoneList = phoneList == null ? null : new TreeMap( (TreeMap) phoneList ); // creates the same mappings } Employee( String first, String last, Date dob, Employee[] fnds, Auto[] ats, Cat c ) { firstName = first; lastName = last; dateOfBirth = dob == null ? null : (Date) dob.clone(); friends = fnds == null ? null : (Employee[]) fnds.clone(); autos = ats == null ? null : (Auto[]) ats.clone(); kitty = c == null ? null : (Cat) c.clone(); } Employee( Employee e ) { firstName = e.firstName; lastName = e.lastName; dateOfBirth = e.dateOfBirth == null ? null : (Date) e.dateOfBirth.clone(); friends = e.friends == null ? null : (Employee[]) e.friends.clone(); autos = e.autos == null ? null : (Auto[]) e.autos.clone(); kitty = e.kitty == null ? null : (Cat) e.kitty.clone(); phoneList = e.phoneList == null ? null : new TreeMap( (TreeMap) e.phoneList ); } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public void addDogToDogs(Dog newDog) { //(I) if ( dogs == null ) dogs = new ArrayList(); dogs.add(newDog); Collections.sort( dogs, new Dog.Dog_Comparator() ); //(J) } public String toString() { String str = ""; if ( dogs != null ) { str += "\nDOGS: "; ListIterator iter = dogs.listIterator(); while ( iter.hasNext() ) str += (Dog) iter.next(); str += "\n"; } if ( autos != null ) { str += "\nAUTOS: "; for ( int i=0; i<autos.length - 1; i++ ) { str += " " + autos[i] + ","; } str += " " + autos[autos.length - 1]; str += "\n"; } if ( friends != null ) { str += "\nFRIENDS:"; for ( int i=0; i<friends.length; i++ ) { str += "\n"; str += friends[i].getFirstName(); str += " " + friends[i].getLastName(); } str += "\n"; } if ( kitty != null ) { str += "\nCAT:"; str += kitty; } if ( phoneList != null ) { str += "\nPhone List:"; str += phoneList; } return "\nFirst Name: " + firstName + "\nLast Name: " + lastName + "\n" + str + "\n"; } } //////////////////////////// class Auto ///////////////////////////// class Auto { String autoBrand; Employee owner; public Auto( String brand ) { autoBrand = brand; } public Auto( String brand, Employee e ) { autoBrand = brand; owner = e; } public String toString() { return autoBrand; } } //////////////////////////// class Poodle /////////////////////////// class Poodle extends Dog { private Employee owner; private double weight; //(K) private double height; public Poodle( Employee owner, String name, int age, double weight, double height ) { super( name, age ); this.owner = owner; this.weight = weight; this.height = height; } public Object clone() { Poodle poo = null; poo = ( Poodle ) super.clone(); return poo; } public String toString() { return super.toString() + " Pedigree: " + "Poodle " + " Weight: " + weight + " Height: " + height ; } public double getDogCompareParameter() {return weight;} //(L) } ////////////////////////// class Chihuahua ///////////////////////////////// class Chihuahua extends Dog { private Employee owner; private double weight; //(M) private double height; public Chihuahua ( Employee owner, String name, int age, double weight, double height ) { super ( name, age ); this.owner = owner; this.weight = weight; this.height = height; } public Object clone() { Chihuahua huahua = null; huahua = ( Chihuahua ) super.clone(); return huahua; } public String toString() { return super.toString() + " Pedigree: " + "Chihuahua " + " Weight: " + weight + " Height: " + height ; } public double getDogCompareParameter(){ return weight; } //(N) } ////////////////////////// class Manager //////////////////////////// class Manager extends Employee { private Employee[] workersSupervised; public Manager ( Employee e, ArrayList dogs ) { //(O) super( e ); ListIterator iter = dogs.listIterator(); while ( iter.hasNext() { Object object = iter.next(); try { Poodle p = (Poodle) object; addDogToDogs( (Dog) p.clone() ); } catch(ClassCastException badpoodlecast) { try { Chihuahua c = (Chihuahua) object; addDogToDogs( (Dog) c.clone() ); } catch(ClassCastException badhuahuacast) {} } } } } ///////////////////////// class TestManager ///////////////////////// class TestManager { public static void main( String[] args ) { Employee e1 = new Employee( "Zoe", "Zaphod" ); // name age Dog dog1 = new Dog( "fido", 3); Dog dog2 = new Dog( "spot", 4); Dog dog3 = new Dog( "bruno", 2); Dog dog4 = new Dog( "darth", 1); // emp name age weight height Poodle dog5 = new Poodle( e1, "pooch", 4, 15.8, 2.1); Poodle dog6 = new Poodle( e1, "doggy", 3, 12.9, 3.4); Poodle dog7 = new Poodle( e1, "lola", 3, 12.9, 3.4); Chihuahua dog8 =new Chihuahua( e1, "bitsy", 5, 3.2, 0.3); Chihuahua dog9 =new Chihuahua( e1, "bookum", 5, 7.2, 0.9); Chihuahua dog10=new Chihuahua( e1, "pie", 5, 4.8, 0.7); ArrayList dawgs = new ArrayList(); dawgs.add( dog1 ); dawgs.add( dog2 ); dawgs.add( dog5 ); dawgs.add( dog6 ); dawgs.add( dog3 ); dawgs.add( dog4 ); dawgs.add( dog10 ); dawgs.add( dog7 ); dawgs.add( dog8 ); dawgs.add( dog9 ); Manager m1 = new Manager( e1, dawgs ); Employee e = (Employee) m1; System.out.printIn( e ); // will invoke Employee's toString } }

Executing the TestManager class produces the following output:

      First Name: Zoe      Last Name: Zaphod      DOGS:      Name: bitsy   Age: 5  Pedigree: Chihuahua   Weight: 3.2    Height: 0.3      Name: pie   Age: 5  Pedigree: Chihuahua   Weight: 4.8    Height: 0.7      Name: bookum   Age: 5  Pedigree: Chihuahua   Weight: 7.2    Height: 0.9      Name: doggy   Age: 3  Pedigree: Poodle   Weight: 12.9      Height: 3.4      Name: lola  Age: 3   Pedigree: Poodle   Weight: 12.9      Height: 3.4      Name: pooch  Age: 4   Pedigree: Poodle   Weight: 15.8      Height: 2.1 

As is clear from the output, the Manager Zoe Zaphod only has poodles and chihuahuas for dogs and that these dogs are kept sorted according to their weights.

The test code in the TestManager class used only one ancillary class. Dog. The reader is encouraged to form more complex instances of Managerusing the other ancillary classes shown and then forming scenarios of ownership and transfer of objects of different types among employees and Managers.

[19]The actual C++ code shown in the previous section also corresponds to the hierarchy of Figure 15.14 since we left the implementation of the classes SalesManager and SalesPerson in the class diagram of Figure 15.13 as exercises for the reader. The reader is urged to add the SalesManager and SalesPerson classes of Figure 15.13 to the code shown in this section also and to provide Java implementation of the SalesManager—SalesPerson feature mentioned at the beginning of the previous section.

[20]See Chapter 3 on the behavior of nested and inner classes vis-a-vis the enclosing classes.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net