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
- References
- Meyer's Criteria for Evaluating for Modularity
- Problem 1
- Inheritance verse Composition
- Issue: Names
- Issue: IO in classes
- Issues with Separators
- Problem 2
- Issue: Improper Inheritance
- Issue: Struct in Class Clothing
- Indentation, White space
- Comments
- Commenting Routines
Various student papers
- Decomposability
- Composability
- Understandability
- Continuity
- Protection
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
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
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 );
}
}
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 ();
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}
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();
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 );
}
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;
}
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 );
}
}
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
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?
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 )
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() );
}
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 );
}
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
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
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();
}
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 );
}
}
}
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 );
}
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;
}
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();
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 );
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"