CS535 Object-Oriented Programming & Design
Fall Semester, 1996
Doc 10, Comments on Assignment 1
[To Lecture Notes Index]
San Diego State University -- This page last updated Oct 1, 1996
Contents of Doc 10, Comments on Assignment 1
- References
- Meyer's Criteria for Evaluating for Modularity
- Indentation, White space
- Names
- Comments
- Kinds of Comments
- Commenting Efficiently
- Commenting Techniques
- Commenting Data Declarations
- Commenting Routines
- Returning Flags
- Fields as Global Variables
- IO in Classes
- How do you implement a Queue?
- Queue Class
- Efficient grow
- Problem 1 String Replacement
Various student papers
Code Complete, Steve McConnell, Microsoft Press, 1993, chapters 9 and
19
- Decomposability
- Composability
- Understandability
- Continuity
- Protection
public class Assgn1_Part1
{
public static void main(String args[])
{
Console.println();
// Initialize strings
DataString A=new DataString('A'); // This will prompt
===>the user for a string.
DataString B=new DataString('B'); // This will prompt
===>the user for a string.
DataString C=new DataString('C'); // This will prompt
===>the user for a string.
String D="";
int i=0;
do
{
{
String a;
String b;
int j=0;
// Check each character of A
do
{
a = A.Data.substring(i+j,i+j+1);
b = B.Data.substring(j,j+1);
j++;
}
// If a letter matches, see if the whole string B is contained
while (a.toLowerCase().equals(b.toLowerCase())
&&(j<B.Data.length()))
if (a.toLowerCase().equals(b.toLowerCase())
&&(j<B.Data.length()))
{
// Place C into D
D=D+C.Data;
i+=j;
}
else
{
// Place the letter from A into D
D=D+A.Data.substring(i,i+1);
i++;
}
}
}
// Do until the end of A is too short to contain B
while (i<=(A.Data.length()-B.Data.length()));
// Place the remainder of A into D
D=D+A.Data.substring(i,A.Data.length());
Console.println(D+"\n");
}
}
Indentation, White space
Use
white space to make program readable
Use indentation to show the program structure
while (a.toLowerCase().equals(b.toLowerCase())
&&(j<B.Data.length()))
if (a.toLowerCase().equals(b.toLowerCase())
&&(j<B.Data.length()))
verses
while ( ( a.toLowerCase().equals( b.toLowerCase() ) ) &&
( j<B.Data.length() )
)
if ( ( a.toLowerCase().equals( b.toLowerCase() ) ) &&
( j<B.Data.length() )
)
{
// Code deleted
}
verses
while ( ( a.toLowerCase().equals( b.toLowerCase() ) ) &&
( j<B.Data.length() )
)
{
// Code deleted
}
Example 1
String A;
String B;
String C;
String D;
verses
String strOriginal;
String strSearch;
String strReplace;
String strNew;
Example 2
private void growQueue() {
temp = new char[ size ];
verses
private void growQueue() {
char[] newQueueSpace = new char[ newQueueSize ]
Example 3
class Buffer {
char[] buff;
// I removed the rest of the class
}
in driverProgram() {
Buffer buffr = new Buffer()
verses
class CharQueue {
char[] queueElements;
// blah blah blah
}
in driverProgram() {
CharQueue buffer = new CharQueue();
Names
"Finding good names is the hardest part of OO Programming"
"Names should fully and accurately describe the entity the variable
represents"
What role does the variable play in the program?
Data Structure | Role, function |
InputRec | EmployeeData |
BitFlag | PrinterReady |
Queue | buffer |
Some Examples of Names, Good and Bad
TrainVelocity | Velt, V, X, Train |
CurrentDate | CD, Current, C, X, Date |
LinesPerPage | LPP, Lines, L, X |
private int INITIAL_SIZE = 32; // initial size of buffer
TempB = B.toLowerCase(); // get lower case of string
buffer[head] = ch; // insert the char
x = x * 2 // multilpy x by 2
verses
private int INITIAL_BUFFER_SIZE = 32;
TempB = B.toLowerCase(); // comparison is case insensitive
// compare uppercase strings
queueElements[ head ] = newQueueItem;
// doubling queueSize results in O(1) cost per insertion
queueSize = queueSize * 2
Comments
"Comments are easier to write poorly than well, and comments can be more
damaging than helpful"
What does this do?
for i := 1 to Num do
MeetsCriteria[ i ] := True;
for i := 1 to Num / 2 do begin
j := i + i;
while ( j <= Num ) do begin
MeetsCriteria[ j ] := False;
j := j + i;
end;
for i := 1 to Mun do
if MeetsCriteria[ i ] then
writeln( i, ' meets criteria ' );
How many comments does this need?
for PrimeCandidate:= 1 to Num do
IsPrime[ PrimeCandidate] := True;
for Factor:= 1 to Num / 2 do begin
FactorableNumber := Factor + Factor ;
while ( FactorableNumber <= Num ) do begin
IsPrime[ FactorableNumber ] := False;
FactorableNumber := FactorableNumber + Factor ;
end;
end;
for PrimeCandidate:= 1 to Num do
if IsPrime[ PrimeCandidate] then
writeln( PrimeCandidate, ' is Prime ' );
Good Programming Style is the Foundation of Well Commented Program
X := X + 1 /* add one to X
/* if allocation flag is zero */
if ( AllocFlag == 0 ) ...
- Explanation of how code works
- Used to explain complicated or tricky code
*p++->*c = a
/* first we need to increase p by one, then ..
-
-
- Make code simpler before commenting
(*(p++))->*c = a
ObjectPointerPointer++;
ObjectPointer = *ObjectPointerPointer;
ObjectPointer ->*DataMemberPointer = a;
/* **** Need to add error checking here **** */
- Distills a few lines of code into one or two sentences
-
-
-
- Description of the code's intent
- Explains the purpose of a section of code
{ get current employee information } intent
{ update EmpRec structure } what
- Use styles that are easy to maintain
/***********************************
* module: Print *
* *
* author: Roger Whitney *
* date: Sept. 10, 1995 *
* *
* blah blah blah *
* *
***********************************/
/***********************************
module: Print
author: Roger Whitney
date: Sept. 10, 1995
blah blah blah
***********************************/
Commenting Individual Lines
Avoid self-indulgent comments
MOV AX, 723h ; R. I. P. L. V. B.
Endline comments have problems
MemToInit := MemoryAvailable(); { get memory available }
- Not much room for comment
-
- Must work to format the comment
Use endline comments on
- Data declarations
-
- Maintenance notes
-
- Mark ends of blocks
Commenting Paragraphs of Code
Write comments at the level of the code's intent
Comment the why rather than the how
Make every comment count
Document surprises
Avoid abbreviations
How verses Why
How
/* if allocation flag is zero */
if ( AllocFlag == 0 ) ...
Why
/* if allocating a new member */
if ( AllocFlag == 0 ) ...
Even Better
/* if allocating a new member */
if ( AllocFlag == NEW_MEMBER ) ...
Summary comment on How
{ check each character in "InputStr" until a
dollar sign is found or all characters have
been checked }
Done := false;
MaxPos := Length( InputStr );
i := 1;
while ( (not Done) and (i <= MaxLen) ) begin
if ( InputStr[ i ] = '$' ) then
Done := True
else
i := i + 1
end;
Summary comment on Intent
{ find the command-word terminator }
Done := false;
MaxPos := Length( InputStr );
i := 1;
while ( (not Done) and (i <= MaxPos ) ) begin
if ( InputStr[ i ] = '$' ) then
Done := True
else
i := i + 1
end;
Summary comment on Intent with Better Style
{ find the command-word terminator }
FoundTheEnd := false;
MaxCommandLength := Length( InputStr );
Index := 1;
while ((not FoundTheEnd) and
(Index <= MaxCommandLength)) begin
if ( InputStr[ Index ] = '$' ) then
FoundTheEnd := True;
else
Index := Index + 1;
end;
Comment the units of numeric data
Comment the range of allowable numeric values
Comment coded meanings
var
CursorX: 1..MaxCols; { horizontal screen position of cursor }
CursorY: 1..MaxRows; { vertical position of cursor on screen }
AntennaLength: Real; { length of antenna in meters: >= 2 }
SignalStrength: Integer; { strength of signal in kilowatts: >= 1 }
CharCode: 0..255; { ASCII character code }
CharAttib: Integer; { 0=Plain; 1=Italic; 2=Bold }
CharSize: 4..127; { size of character in points }
Comment limitations on input data
Document flags to the bit level
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
procedure InsertionSort
{
Var Data: SortArray; { sort array elements }
FirstElement: Integer {index of first element to sort}
LastElement: Integer {index of last element to sort}
}
public char removeChar()
{
if ( isQueueEmpty() == true )
return '\u0000';
else
return queueElements[ queueFront-- ]
}
verses
class CharQueue
{
// Select a rare unicode for remove char from empty
// queue flag
public static final char EMPTY = \u0000';
public char removeChar()
{
if ( isQueueEmpty() == true )
return EMPTY ;
else
return queueElements[ queueFront-- ]
}
}
public void static main( String arg[] )
{
blah blah blah
char fromQueue = buffer.removeChar();
if ( fromQueue == CharQueue.EMPTY )
do something proper
else
the real code goes here
Issue Who checks for Empty Queue?
Options
- Queue class only
- User code only
- Both user code and queue class
Queue Class only
Queue class can not know the proper response to removing a character from an
empty queue!
This option does not work
Both User and Queue
See previous slide
User Code Only
User code
public void static main( String arg[] )
{
blah blah blah
if ( buffer.isEmpty() == false )
fromQueue = buffer.removeChar();
else
error condition, handle it here
Queue code
class CharQueue
{
public char removeChar()
{
return queueElements[ queueFront-- ]
}
How to tell User to check
Documentation
/** User must insure that the queue is not empty before calling
* removeChar
* @return first character in the queue
*/
public char removeChar()
{
return queueElements[ queueFront-- ]
}
Assert
public char removeChar()
{
Assert.condition( isQueueEmpty() == false );
return queueElements[ queueFront-- ]
}
C/C++ have an assert call
In Java we have to build our own
Exceptions will modify this
final class Assert {
public static void condition( boolean expression )
{
if ( expression == false )
warn user some how
}
}
class ThreeStrings {
static String A, B, C, D;
static void ReadStrings() {
A = Console.readLine( "Enter string A: " );
B = Console.readLine( "Enter string B: " );
C = Console.readLine( "Enter string C: " );
}
static void ReplaceStrings() {
// Code here to do the string replacement
D = stringReplaced;
}
static void OutputString() {
Console.println( "\nString D is: " + D );
}
public static void main( String args[] ) {
ReadStrings();
ReplaceStrings();
OutputString();
}
}
Remove the Globals
Now we can use the class elsewhere!
class ThreeStrings {
static void ReadStrings( String A, String B, String C ) {
A = Console.readLine( "Enter string A: " );
B = Console.readLine( "Enter string B: " );
C = Console.readLine( "Enter string C: " );
}
public static String ReplaceStrings( String A,
String B,
String C
) {
// Code here to do the string replacement
return stringReplaced;
}
public static void main( String args[] ) {
String A, B, C, D;
ReadStrings( A, B, C );
D = ReplaceStrings( A, B, C );
Console.println( "\nString D is: " + D );
}
}
More Fields as Global Variables
class Queue {
private char[] elements;
private int queueFront;
private int queueRear;
private int queueCount;
private char[] tempQueue;
private void growQueue()
{
tempQueue = new char[ elements.length * 2 ];
for ( int k = 0; k < elements.length; k++ )
tempQueue[ k ] = elements[ k ];
elements = tempQueue;
}
}
Just declare tempQueue locally, not as field in class
private void growQueue()
{
char[] tempQueue;
tempQueue = new char[ elements.length * 2 ];
for ( int k = 0; k < elements.length; k++ )
tempQueue[ k ] = elements[ k ];
elements = tempQueue;
}
Even More Fields as Global Variables
class WrongWay {
private int argumentOne; // used only in helper and
private int argumentTwo; // FunctionThatNeedsHelper
public void FunctionThatNeedsHelper() {
// do some stuff
// need to call helper function that needs two parameters
// so save parameters in fields
argumentOne = blah;
argumentTwo = moreBlah;
// now call helper method
result = helper();
}
private int helper() {
// now read parameters
return argumentOne / argumentTwo + 10;
}
}
class BetterWay {
public void FunctionThatNeedsHelper() {
result = helper( blah, moreBlah );
}
private int helper(int a, int b ) {
return a / b + 10;
}
}
Example 1
public void enqueue( )
{
char charToAdd;
charToAdd = Console.readLine("Enter char for queue").charAt(0);
if ( isQueueFull() == true )
growQueue();
elements[ queueRear ] = charToAdd;
queueRear = ( queueRear + 1 ) % queueSize();
elementCount++;
}
Example 2
public void enqueue( char charToAdd )
{
Console.println( "Adding char to queue " + charToAdd );
if ( isQueueFull() == true )
growQueue();
elements[ queueRear ] = charToAdd;
queueRear = ( queueRear + 1 ) % queueSize();
elementCount++;
Console.println( "queueRear is now: " + queueRear );
}
Example 3
public void print()
{
Console.print( "Front " + queueFront );
Console.print( " Rear " + queueRear );
Console.println( " " + String.valueOf( queueElements ) );
}
public void enqueue( char charToAdd )
{
char[] newQueue = new char[ queueSize() + 1];
for ( int i = 0; i < queueSize(); i++ )
newQueue[ i + 1 ] = queueElements[ i ];
queueElements = newQueue;
queueElements[ 0 ] = charToAdd;
queueRear = queueRear + 1;
elementCount++;
}
class CharQueue
{
/* Class invariant,
* queueRear is the location the next queue item should be placed
* If the queue is not empty, queueFront is the location of the first
* item in the queue
*/
private char[] queueElements;
private int queueFront;
private int queueRear;
private int elementCount;
public static final int DEFAULT_QUEUE_SIZE = 256;
public CharQueue( int Size )
{
queueElements = new char[ Size ];
queueFront = 0;
queueRear = 0;
elementCount = 0;
}
public CharQueue( )
{
this( DEFAULT_QUEUE_SIZE );
}
//Queue Operations
public char dequeue( )
{
char itemRemoved = queueElements[ queueFront ];
queueFront = ( queueFront + 1 ) % queueCapacity();
elementCount--;
return itemRemoved;
}
public void enqueue( char itemToAdd )
{
if ( isFull() == true )
growQueue();
queueElements[ queueRear] = itemToAdd;
queueRear = ( queueRear + 1 ) % queueCapacity();
elementCount++;
}
public boolean isFull()
{
if ( elementCount >= queueCapacity() )
return true;
else
return false;
}
public boolean isEmpty()
{
if ( elementCount == 0 )
return true;
else
return false;
}
//Queue Operations
public String toString()
{
return "Front " + queueFront +
"Rear " + queueRear + " " +
String.valueOf( queueElements );
}
private void growQueue()
{
CharQueue newQueue;
newQueue = new CharQueue( queueCapacity() * 2 );
while ( isEmpty() == false )
{
newQueue.enqueue( dequeue() );
}
queueElements = newQueue.queueElements;
queueFront = newQueue.queueFront;
queueRear = newQueue.queueRear;
elementCount = newQueue.elementCount;
}
private int queueCapacity()
{
return queueElements.length;
}
}
Using the Queue Class
class QueueTest
{
public static void main( String[] args )
{
String testData = "abcdefghijklmnopqrstuvwxyz";
int nextLetter = 0;
CharQueue buffer = new CharQueue( 4 );
buffer.enqueue( '1');
buffer.enqueue( '2');
buffer.enqueue( '3');
for ( int k = 0; k < 3; k++ )
{
buffer.enqueue( testData.charAt( nextLetter++ ) );
buffer.dequeue( );
System.out.println( buffer );
}
for ( int k = 0; k < 9; k++ )
{
buffer.enqueue( testData.charAt( nextLetter++ ) );
System.out.println( buffer );
}
for ( int k = 0; k < 8; k++ )
{
System.out.println( buffer.dequeue( ) );
}
for ( int k = 0; k < 14; k++ )
{
buffer.enqueue( testData.charAt( nextLetter++ ) );
System.out.println( buffer );
}
}
}
/* Assumes called only when queue is full. Thus
* queueFront = queueRear
* Use native method to copy arrays:
* arraycopy(src, srcPosition,dest,destPosition, length)
* Parameters:
* src - the source data
* srcpos - start position in the source data
* dest - the destination
* destpos - start position in the destination data
* length - the number of array elements to be copied
*/
private void growQueue()
{
char[] newQueue = new char[ queueCapacity() * 2 ];
int elementsFromFrontToEnd = queueCapacity() - queueFront;
System.arraycopy( queueElements, queueFront,
newQueue, 0,
elementsFromFrontToEnd);
System.arraycopy( queueElements, 0,
newQueue, elementsFromFrontToEnd,
queueRear);
queueElements = newQueue;
queueFront = 0;
queueRear = elementCount;
}
class StringManipulation
{
public static final int NOT_FOUND = -1;
private String text;
private boolean ignoreCase = true;
public StringManipulation( String textToManipulate )
{
text( textToManipulate );
}
public ignoreCase( boolean yesOrNo )
{
ignoreCase = yesOrNo;
}
public String toString()
{
return text();
}
private void text( String newText )
{
text = newText;
}
private String text( )
{
return text;
}
// More String
public int indexOf( String pattern, int fromIndex )
{
for ( int searchIndex = fromIndex;
searchIndex <= text().length() - pattern.length();
searchIndex ++
)
{
boolean match;
match = text().regionMatches( ignoreCase, searchIndex ,
pattern, 0, pattern.length() );
if ( match == true )
return index;
}
return NOT_FOUND;
}
public void replaceFirst( String searchPattern,
String replacePattern
)
{
int searchIndex = indexOf( searchPattern, 0 );
if ( searchIndex != NOT_FOUND )
{
StringBuffer newText = new StringBuffer();
newText.append( text().substring( 0, searchIndex) );
newText.append( replacePattern );
newText.append( text().substring( searchIndex +
searchPattern.length() )
);
text( newText.toString() );
}
}
// Problem 1
public void replaceAll( String searchPattern,
String replacePattern
)
{
StringBuffer newText = new StringBuffer();
int startIndex = 0;
int searchIndex = indexOf( searchPattern, startIndex );
while ( searchIndex != NOT_FOUND )
{
newText.append( text().substring( startIndex, searchIndex) );
newText.append( replacePattern );
startIndex = searchIndex + searchPattern.length();
searchIndex = indexOf( searchPattern, startIndex );
}
newText.append( text().substring( startIndex ) );
text( newText.toString() );
}
// Start of Problem 3
public String grepLikeFind( String startPattern,
String endPattern,
int fromIndex
)
{
int startIndex = indexOf( startPattern, fromIndex );
if ( startIndex == NOT_FOUND )
return "";
int endPatternIndex;
endPatternIndex = indexOf( endPattern,
startIndex + startPattern.length()
);
if ( endPatternIndex == NOT_FOUND )
return "";
return text().substring( startIndex,
endPatternIndex + endPattern.length()
);
}
} // End class
How about Undo!
class StringManipulation
{
public static final int NOT_FOUND = -1;
private String text;
private boolean ignoreCase = true;
private Stack changeHistory = new Stack();
private void text( String newText )
{
changeHistory.push( text );
text = newText;
}
public void undo()
{
text = ( String ) changeHistory.pop();
}
// Rest of class the same