SDSU CS535 Object-Oriented Programming & Design
Fall Semester, 1996
Doc 19, Comments on Assignment 2

[To Lecture Notes Index]
San Diego State University -- This page last updated Nov 5, 1996
----------

Contents of Doc 19, Comments on Assignment 2

  1. References
  2. Meyer's Criteria for Evaluating for Modularity
  3. Problem 1
    1. Inheritance verse Composition
    2. Issue: Names
    3. Issue: IO in classes
    4. Issues with Separators
  4. Problem 2
    1. Issue: Improper Inheritance
    2. Issue: Struct in Class Clothing
    3. Indentation, White space
  5. Comments
    1. Commenting Routines

Doc 19, Comments on Assignment 2 Slide # 1

References


Various student papers



Doc 19, Comments on Assignment 2 Slide # 2

Meyer's Criteria for Evaluating for Modularity



Doc 19, Comments on Assignment 2 Slide # 3Listen Here!
Symptoms of Serious Diseases

Very long methods - page(s) long

Using IO in "data" classes
public void putKeyValues( )
   {
   String key = Console.readString( "Enter a key " );
   String value = Console.readString( "Enter a value " );

   table.put( key, value );
   }


Mixing of test or driver program with "data" classes

Doc 19, Comments on Assignment 2 Slide # 4Listen Here!

Problem 1


Implement and test a key-value class(es).
This class(es) stores keys and values.
Assume that all keys and values are strings.
A key-value object should be able to create a string of all its keys and values.
From such a string, the key-value class(es), must be able to recreate the original object.
Issue: Relation to hashtable

Inheritance verse Composition

class StringHashtable extends Hashtable { // Inheritance

   public void put( String key, String value ) {
      super.put( key, value );
   }

   public String get( String key, String value ) {
      return (String) super.put( key, value );
   }
}


class StringHashtable {      // Composition

   Hashtable strings = new Hashtable();
   
   public void put( String key, String value ){
      strings.put( key, value );
   }

   public String get( String key, String value ) {
      return (String) strings.put( key, value );
   }
}


Doc 19, Comments on Assignment 2 Slide # 5Listen Here!
Inheritance

Less work

The following is a working solution to problem 1
class KeyValue extends Hashtable {
   KeyValue() {};
   
   KeyValue( String keyValueString ) {
      StringTokenizer parse;
      parse = new StringTokenizer( keyValueString, "{=,}" );
      
      while ( parse.hasMoreElements() )
         put( parse.nextToken().trim() , parse.nextToken() );
}
}
Allows for subtyping

Hashtable grades = new KeyValue ();


Doc 19, Comments on Assignment 2 Slide # 6Listen Here!
Inheritance Solution
class KeyValue extends Hashtable {
   KeyValue() {};
   
   KeyValue( String keyValueString ) {
      StringTokenizer parse;
      parse = new StringTokenizer( keyValueString, "{=,}" );
      
      while ( parse.hasMoreElements() )
         put( parse.nextToken().trim() , parse.nextToken() );
   }
}
class Test  {
   public static void main( String args[] )  {
      KeyValue pets = new KeyValue();
      pets.put( "Sam", "cat" );
      pets.put( "George", "ground hog" );
      pets.put( "Liz", "lizard" );
      System.out.println( pets );
      
      String petsString = "Liz=lizard,Sam=cat,George=ground hog";
      KeyValue recoveredPets = new KeyValue( petsString );
      System.out.println( recoveredPets );

      petsString = "Liz=lizard,Sam=cat=George=ground hog";
      recoveredPets = new KeyValue( petsString );
      System.out.println( recoveredPets );
   }
}
Output
{Liz=lizard, Sam=cat, George=ground hog}
{Liz=lizard, Sam=cat, George=ground hog}
{Liz=lizard, Sam=cat, George=ground hog}


Doc 19, Comments on Assignment 2 Slide # 7Listen Here!
Composition

Better control over which methods are in public interface

At times you do not want to inherit all methods of super class

Do you want someone to put student records in a StringHashtable?

Subtyping achievable via interfaces
class Hashtable implements Indexable[1] {
   // code removed
}
class StringHashtable implements Indexable {      // Composition
   Hashtable strings = new Hashtable();
}

Hashtable grades = new StringHashtable();

Doc 19, Comments on Assignment 2 Slide # 8Listen Here!
Composition Solution
class StringHashtable
   {
   protected Hashtable table = new Hashtable();
   protected char keyValueSeparator  =  '=' ;
   protected char pairSeparator  =  ';' ;
   
   public StringHashtable( ) {};
   
   public StringHashtable( String hashString ) throws ParseException
      {
      fromString( hashString );
      }

   public StringHashtable( String hashString,
                     char keyValueSeparator,
                     char pairSeparator ) throws ParseException
      {
      separators( keyValueSeparator,  pairSeparator ); 
      fromString( hashString );
      }

   // Standard hashtable methods
   public int size()                   { return table.size(); }
   public boolean isEmpty()          { return table.isEmpty(); }
   public Enumeration elements()       { return table.elements(); }
   public Enumeration keys()          { return table.keys(); }
   public boolean contains(String value)    { return table.contains( value ); }
   public boolean containsKey(String key)   { return table.containsKey( key ); }
   public String remove(String key)       { return (String) table.remove( key); }
   public void clear()                { table.clear(); }   

   public void put( String key, String value )
      {
      table.put( key, value );
      }

   public String get( String key )
      {
      return (String) table.get( key );
      }


Doc 19, Comments on Assignment 2 Slide # 9

   public String toString()
      {
      Enumeration keys = keys();
      StringBuffer buffer = new StringBuffer( 4*table.size() );

      while ( keys.hasMoreElements()  ) 
         {
         String key = keys.nextElement().toString();
         buffer.append( key );
         buffer.append( keyValueSeparator );
         buffer.append( get( key ) );
         buffer.append( pairSeparator );
         }
      
      // don't need PairSeparator at end of string
      buffer.setLength( buffer.length() - 1 );
      return buffer.toString();
      }

   public void separators( char keyValueSeparator, char pairSeparator )
      {
      this.keyValueSeparator = keyValueSeparator;
      this.pairSeparator = pairSeparator;
      }

   protected boolean isKeyValueSeparator( String separator )
      {
      if ( ( separator.length() == 1) && 
          ( separator.charAt( 0 ) == keyValueSeparator )
         )
         return true;
      else
         return false;
      }

   protected boolean isPairSeparator( String separator ) {
      if ( ( separator.length() == 1) && 
          ( separator.charAt( 0 ) == pairSeparator )
         )
         return true;
      else
         return false;
      }


Doc 19, Comments on Assignment 2 Slide # 10

   public void fromString( String hashString ) throws ParseException
      {
      StringTokenizer parser;
      String separators = "" + keyValueSeparator + pairSeparator;
      boolean returnDelimiter = true;
      
      parser = new StringTokenizer( hashString, separators, returnDelimiter );
      
      while ( parser.hasMoreTokens() == true )
         {
         String key = parser.nextToken();

         if ( isKeyValueSeparator( parser.nextToken() ) == false )
            throw new ParseException( "Wrong separator after: " + key + 
                                 " in \n\t" + hashString );
            
         String value = parser.nextToken();
         
         if ( parser.hasMoreTokens() == true )
            if ( isPairSeparator( parser.nextToken() ) == false )
               throw new ParseException(  "Wrong separator after: " + value + 
                                 " in \n\t" + hashString );
         put( key, value );
         }
      }
   } // End class


class ParseException extends java.lang.Exception
   {
   public ParseException() { };
   public ParseException( String message )
      {
      super( message );
      }
   }

Doc 19, Comments on Assignment 2 Slide # 11
Testing
class Test 
{
   public static void main( String args[] ) 
   {
   try
      {
      StringHashtable pets = new StringHashtable();
      pets.put( "Sam", "cat" );
      pets.put( "George", "ground hog" );
      pets.put( "Liz", "lizard" );
      System.out.println( pets );
      
      String petsString = "Liz=lizard;Sam=cat;George=ground hog";
      StringHashtable recoveredPets = new StringHashtable( petsString );
      System.out.println( recoveredPets );

      petsString = "Liz=lizard;Sam=cat=George=ground hog";
      recoveredPets = new StringHashtable( petsString );
      System.out.println( recoveredPets );
      }
   catch ( Exception problem )
      {
      System.out.println( problem );
      }
   }
}
Output
Liz=lizard;Sam=cat;George=ground hog
Liz=lizard;Sam=cat;George=ground hog
ParseException: Wrong separator after: cat in 
   Liz=lizard;Sam=cat=George=ground hog


Doc 19, Comments on Assignment 2 Slide # 12Listen Here!
What is the Difference?Which to use Here?


class KeyValue extends Hashtable
KeyValue is a collection that maps keys (of type object) to values (of type object)
If all keys & values are strings then the output of toString() method can be used to recreate the original KeyValue object


class StringHashtable
StringHashtable is a collection that maps keys (of type string) to values (of type string)
The output of toString() method can be used to recreate the original StringHashtable object


Are your clients trust worthy?

How important is subtyping?

How much time is available?

Do you want/need all operation of hashtable?


Doc 19, Comments on Assignment 2 Slide # 13Listen Here!

Issue: Names

Standard Names
public void put( String key, String value )
   {
   table.put( key, value );
   }

public void putKeyInTable( String key, String value )
   {
   table.put( key, value );
   }

IssueMeaning of Names
public void closeFile() {

   /* Try to open the output file
   try {
      FileOutputStream output = new FileOutputStream( "cs535" );
      // rest deleted


public String removeKeyValue( String key ) 
{
   return (String) table.get( key );
}


class Assignment2Problem2Hash
{
   // Code not copied


public Grade( int a, int b, int c, int d, int e, int f )

public student( String fn, String ln, String idn )

public student( String firstName, String lastName, String IDNumber )

Doc 19, Comments on Assignment 2 Slide # 14Listen Here!

Issue: IO in classes


An Error
public void put( String key, String value )
   {
   table.put( key, value );
   System.out.println( "Just entered key " + key + " and value " + value );
   }

A Near Fatal Disease
public void put( )
   {
   String key = Console.readString( "Enter a key " );
   String value = Console.readString( "Enter a value " );

   table.put( key, value );
   }

Type of Acceptable IO in a Data Class
/**
 *  Write an ascii representation of object to an output stream
 */
public void saveObject( OutputStream out )
   {
   PrintStream printOut = new PrintStream( out );
   printOut.print( this.toString() );
   }

Doc 19, Comments on Assignment 2 Slide # 15Listen Here!

Issues with Separators

Constants??
   public void fromString( String hashString )
      {
      StringTokenizer parser;
      
      parser = new StringTokenizer( hashString, ";=" );
      
      while ( parser.hasMoreTokens() == true )
         {
         String key = parser.nextToken();
         String value = parser.nextToken();
         put( key, value );
         }
      }

Using two Separators to Check for Errors
      while ( parser.hasMoreTokens() == true )
         {
         String key = parser.nextToken();

         if ( isKeyValueSeparator( parser.nextToken() ) == false )
            throw new ParseException( "Wrong separator after: " + key + 
                                 " in \n\t" + hashString );
            
         String value = parser.nextToken();
         
         if ( parser.hasMoreTokens() == true )
            if ( isPairSeparator( parser.nextToken() ) == false )
               throw new ParseException(  "Wrong separator after: " + value + 
                                 " in \n\t" + hashString );
         put( key, value );
         }

Doc 19, Comments on Assignment 2 Slide # 16Listen Here!
Issues with SeparatorsCan the Client Select the Separators?

Possible situations:
StringHashtable controls the separators, clients have no say in what the separators are
StringHashtable has default separators, client can change defaults
StringHashtable has no separators, client must always supply separators

What to do if separator appears in key or value?

State that class does not handle that situation
Makes parsing easier
Possible source of errors
Do you have trust worthy clients?

Use special character to mark separators in key/value strings
Let separators be ",="
Use ! as special escape character
The key "whitney,Roger" becomes "whitney!,Roger"
Parser has to add escape character when creating string and remove them when reading string


Doc 19, Comments on Assignment 2 Slide # 17Listen Here!

Problem 2


The student record for a course contains the student's first and last names, student id number, and the grades for the programs, assignments, and tests. Each graded event (programs, assignments, and tests) has a name (like program 1) and a score. The student record must be able to compute the average of the scores. Assume that all graded events have the same weight. As in part 1) of the assignment, we need to be able to convert a student record object to a string and the string back to a student record object.
Issue How to Use Problem 1 Here?

Forget problem 1 and just parse in a Student class

Create a IntegerHashtable with keys a string and values an Integer

Extend StringHashtable to handle ints as values

Force clients of StringHashtable to convert between ints and strings

Doc 19, Comments on Assignment 2 Slide # 18Listen Here!
Lazy Solution
class Student
   {
   protected StringHashtable studentData = new StringHashtable();
   protected StringHashtable grades = new StringHashtable();
   
   protected String studentGradeSeparator = "|";
   
   protected static final String FIRST_NAME = "firstName";
   protected static final String LAST_NAME = "lastName";
   protected static final String ID = "ID";

   public Student( String studentString )   throws ParseException
      {
      fromString( studentString );
      }
      
   public Student( String firstName, String lastName, String IDNumber )
      {
      studentData.put( FIRST_NAME, firstName );
      studentData.put( LAST_NAME, lastName );
      studentData.put( ID, IDNumber );
      } 
      
   public void setGradeAt( String eventName, int grade )
      {
      grades.put( eventName, String.valueOf( grade ) );
      }
      
   public int gradeAt( String eventName )
      {
      Integer grade = new Integer( grades.get( eventName ) );
      return grade.intValue();
      }

   public String toString()
      {
      return studentData.toString() + studentGradeSeparator +
            grades.toString();
      }


Doc 19, Comments on Assignment 2 Slide # 19

   public void  fromString( String aStudent ) throws ParseException
      {
      int separatorIndex = aStudent.indexOf(studentGradeSeparator);
      int notFound = -1;
      
      if ( separatorIndex == notFound ) 
         throw new ParseException( "No student grade separator in: \n\t" + 
                                 aStudent);
         
      studentData.fromString( aStudent.substring( 0,    separatorIndex ));
      grades.fromString( aStudent.substring(    separatorIndex + 1 ));
      }
   }

class Test 
{
   public static void main( String args[] ) 
   {
   try
      {
      Student roger = new Student( "Roger", "Whitney", "000");
      roger.setGradeAt( "exam1", 50 );
      roger.setGradeAt( "program 1", 3 );
      roger.setGradeAt( "term paper", 121 );
      System.out.println( roger );

      String asciiStudent = "firstName=Roger;ID=000;lastName=Whitney|exam1=50;program 1=3;term paper=121";
      Student recovered =  new Student( asciiStudent );
      System.out.println( recovered );
      
      }
   catch ( Exception problem )
      {
      System.out.println( problem );
      }
   }
}

Doc 19, Comments on Assignment 2 Slide # 20Listen Here!

Issue: Improper Inheritance


class Student extends StringHashtable 
   {
   protected StringHashtable grades = new StringHashtable();
   
   protected String studentGradeSeparator = "|";
   
   protected static final String FIRST_NAME = "firstName";
   protected static final String LAST_NAME = "lastName";
   protected static final String ID = "ID";

   public Student( String studentString )   throws ParseException
      {
      fromString( studentString );
      }
      
   public Student( String firstName, String lastName, String IDNumber )
      {
      put( FIRST_NAME, firstName );
      put( LAST_NAME, lastName );
      put( ID, IDNumber );
      } 
      

Doc 19, Comments on Assignment 2 Slide # 21Listen Here!

Issue: Struct in Class Clothing

class GradeEvent
   {
   private String type;
   private String name;
   private int grade;

   public GradeEvent( String type, String name, int grade )
      {
      this.type = type;
      this.name = name;
      this.grade = grade;
      }

   public void type( String type )
      {
      this.type = type;
      }

   public String type( )
      {
      return  type;
      }


Doc 19, Comments on Assignment 2 Slide # 22Listen Here!

Indentation, White space

class Student {

 protected StringHashtable studentData = new StringHashtable();

 protected StringHashtable grades = new StringHashtable();

 protected String studentGradeSeparator = "|";

 protected static final String FIRST_NAME = "firstName";

 protected static final String LAST_NAME = "lastName";

 protected static final String ID = "ID";

 public Student( String studentString ) throws ParseException {

  fromString( studentString );

 }

 public Student( String firstName, String lastName, String IDNumber ){

  studentData.put( FIRST_NAME, firstName );

  studentData.put( LAST_NAME, lastName );

  studentData.put( ID, IDNumber );

 } 

 public void setGradeAt( String eventName, int grade ) {

  grades.put( eventName, String.valueOf( grade ) );

 }

 public int gradeAt( String eventName ){

  Integer grade = new Integer( grades.get( eventName ) );

  return grade.intValue();


Doc 19, Comments on Assignment 2 Slide # 23Listen Here!

Comments

Example 1
// Divide by zero check and zero returned means no grades
if ( numberOfEvents == 0 ) 
   return 0;
else
   return sum / numberOfEvents ;

Example 2
/*************************************
*   Author:         Roger Whitney
*   Method Name:   Foobar
*   Change History:   Birthday Oct 12 1996
*************************************/

Example 3
/* The getKeysValues displays the entire contents of the hash table as a single
   string. Keys and their values are delimited by colons while key-values are 
   separated by line feed. This method was designed using an array to store the 
   list of keys since a method to enumerate both the keys and values 
   simultaneously could not be found. Therefore, the keys are stored in 
   ArarysOfKeys, which can grow in increments of 20, prior to enumerating the 
   values. When the values are traversed, the appropriate key is displayed with it
   by taking the value from the array.

   NextValue - enumeration object to remove all the values from table
   NextKey   - enumeration object to remove all the keys from table, which are
             placed in ArrayOfKeys
   ArrayLength   - holds the current length of ArrayOfKeys
   KeyValueNumber   - holds the current keyvalue being processed
   ArrayOfKeys      - array that holds all of the keys
   Value - holds the string from nextValue in order to display on screen
*/

public void getKeysValues()
{
   Enumeration NextValue    = table.elements();
   Enumeration NextKey    = table.keys();
   Integer ArrayLength    = new Integer( 20 );

Doc 19, Comments on Assignment 2 Slide # 24

Commenting Routines


Avoid Kitchen-Sink Routine Prologs

Keep comments close to the code they describe

Describe each routine in one or two sentences at the top of the routine

Document input and output variables where they are declared

Differentiate between input and output data

Document interface assumptions

Keep track of the routine's change history

Comment on the routine's limitation

Document the routine's global effects

Document the source of algorithms that are used

Separate internal and external comments
Internal comments involve implementation details
External comments relate to how to use the method
"Comments are easier to write poorly than well, and comments can be more damaging than helpful"


----------