SDSU CS 535 Object-Oriented Programming & Design
Spring Semester, 1999
Assignment 2 Comments
Previous    Lecture Notes Index    Next    
© 1999, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 21-Mar-99

Contents of Doc 12, Assignment 2 Comments


References

Design Patterns: Elements of Reusable Object-Oriented Software , Gamma, Helm, Johnson, Vlissides, 1995, pp. 163-174 (Composite), pp. 325-330 (Template Method)

Object-Oriented Software Construction , Bertrand Meyer, Prentice Hall, 1988, pp. 115-128 (Programming with Contracts)

Listen Here!S-mar9 1min Doc 12, Assignment 2 Comments Slide # 2

Naming Issues

Example 1

public BooleanQuestion(String message,
   String methodName1,
   String methodName2,
   Object objectName1,
   Object objectName2 )
public BooleanQuestion(String message,
   String trueCallbackMethod,
   Object trueReceiver,
   String falseCallbackMethod,
   Object falseReceiver)

Numbered variables indicate order not the role the variable plays

Example 2

public class ConsoleQuestionThing
public class MenuManager
public class Menu

Listen Here!S-mar9 45secs Doc 12, Assignment 2 Comments Slide # 3
Example 3

pubic void addOption( Option option, 
   Object exetutee, 
   String methodName)
Example 4

String szHeader;
Vector vAvailableOptions;
int iSize;
Hungarian naming style is common in the Window environment. While I personally do not use the style, it is acceptable as long as one is consistent in its usage. See http://ivory.lm.com/~gregleg/hungarian.html or http://www.strangecreations.com//library/c/naming.txt, or http://www.stanford.edu/~croyer/hungarian.html for more information.

Listen Here!S-mar9 54secs Doc 12, Assignment 2 Comments Slide # 4

Reuse Killers

public class StringQuestion 
   {
   public void displayQuestion() 
      {
      try 
         {
         Console.println( theQuestion );
         String response = 
            Console.readln( "Your answer: ");
         
         method.invoke( executee, new String[] {response});
         } 
      catch (IllegalAccessException willNotOccur ) 
         {
         Console.println("Illegal Access of Method");
         Runtime.getRuntime().exit( 1 );
         } 
      catch (IllegalArgumentException wrongType) 
         {
         Console.println("Illegal Access of Method");
         } 
      catch (InvocationTargetException methodLost) 
         {
         Console.println("Could not find method");
         }
      }
   }
Printing error messages to the screen in this class reduces it reusability. The application may not want that to happen, or happen in this way. The exit() is a bigger problem. The application may wish to attempt to recover, it may need to save files before exiting.

Listen Here!S-mar9 12mins Doc 12, Assignment 2 Comments Slide # 5

Runtime Exceptions


Use them sparingly. Your documentation rarely covers them and the compiler does not force code to check for them. This means programmers using your code will not know about them. Hence they will not check them.

Listen Here!S-mar9 1min Doc 12, Assignment 2 Comments Slide # 6

Relevant Heuristics


3.3 Beware of classes that have many accessor methods defined in their public interfaces. Having many implies that related data and behavior are not being kept in one place

A number of people had classes with mainly accessor methods.
2.9 Keep related data and behavior in one place

Not following 2.9 often leads to violating 3.3
2.8 A class should capture one and only one key abstraction

In the object-oriented world an abstraction includes data and behavior


Listen Here!S-mar9 5mins Doc 12, Assignment 2 Comments Slide # 7
Set of Functions
pubic class Question {
   public String stringQuestion( String message ) {
      System.out.println( "message" );
      System.out.println( "Your answer: " );
      String response = Console.readLine().trim();
      return response;
   }
   public boolean booleanQuestion( String message ) {
      try {
         System.out.println( "message" );
         System.out.println( "Your answer: " );
         booelan response = Console.readBoolean();
         Console.flushLine();
         return response;
      } catch (NumberFormatError badInput ) { //blah}
   }
   public int intQuestion( String message ) {
      try { //blah
      } catch (NumberFormatError badInput ) {//more blah }
   }
}
What is the abstraction? Where is the data?

Listen Here!S-mar9 9mins Doc 12, Assignment 2 Comments Slide # 8

Cut & Pasting of Code


There are two major problems of cutting and pasting of code:

The same code is repeated in many locations. Making it hard to change the code. It has to be changed in all N locations. Where are all N locations?

Cutting and pasting code often means you are missing a useful abstraction. Better to find the abstraction.

Types of Things Repeated in Code

Lines of Code
Logic
Algorithms

Listen Here!S-mar9 1min Doc 12, Assignment 2 Comments Slide # 9

Repeating Lines of Code

Before
public void method1() {
   int foo = 12;
   //blah
   statement1;
   statement2;
   statement3;
   // more blah
}
public void method2() {
   int bar = 42;
   //stuff
   statement1;
   statement2;
   statement3;
   // more stuff
}

Doc 12, Assignment 2 Comments Slide # 10
Repeating Lines of CodeAfter

Factoring out the common states sometimes is as easy as given here. Often it requires more thought. The common code operates on a set of values. How to get the values to and from the common method? Sometimes it takes a shift of point of view & abstraction to do this.

public void method1() {
   int foo = 12;
   //blah
   foo = repeated( whatGoesHere );
   // more blah
}
public void method2() {
   int bar = 42;
   //stuff
   bar = repeated( something );
   // more stuff
}
public int repeated(Object agrument) {
   statement1;
   statement2;
   statement3;
   return result;
}

Listen Here!S-mar9 8mins Doc 12, Assignment 2 Comments Slide # 11

Repeating Logic

Example

public void run() {
   while (true) {
      try {
         System.out.print( message );
         int selection = Integer.parseInt( keyboard.readLine() );
         System.out.println();
         execute( selection);
      } catch (InvalidSelectionException ise) {
         //handle invalid selection
      } catch (OptionDisabledException ode ) {
         // handle disabled option
      }
   }
}
private void execute( int selection ) throws IOException {
   if ( !isValid( selection ) )
      throw new InvalidSelectionException("Invalid Selection");
   Option selectedOption = menuOption.at( selection );
   
   if ( !option.isEnabled() )
      throw new OptionDisabledException("Option is  disabled");
   
   //now execute the method
}
Before going on, you should be able to suggest a number of improvements to this code

Doc 12, Assignment 2 Comments Slide # 12
Repeating Logic - After
public void run() {
   while (true) {
      try {
         int selection = getUsersSelection( );
         if ( !isValid( selection ) )
            //handle invalid selection;
         Option selectedOption = menuOption.at( selection );
         if ( !option.isEnabled() )
            // handle disabled option;
         execute( selection);
      } catch (//YouCanFillInTheRest );
   }
}
private int getUsersSelection() {
   System.out.print( message );
   int selection = Integer.parseInt( keyboard.readLine() );
   System.out.println();
   return selection;
}
public void execute( int selection ) throws IOException {
   //now execute the method
}

Listen Here!S-mar9 6mins Doc 12, Assignment 2 Comments Slide # 13

Programming By Contract


Precondition

The constraints under which a routine will function properly
"Push" may not be called if a stack is full
"Pop" may not be applied to an empty stack

Postcondition

Properties of the state resulting from a routine's execution
After a "push," the stack may not be empty, its top is the element just pushed, and its number of elements has been increased by one


Contract

A client must insure that the precondition holds before calling an operation
The server will guarantee that the postcondition holds

The contract forces a clear definition of whose responsibility it is to check every condition required for correct operation

Doc 12, Assignment 2 Comments Slide # 14
What to Do if Precondition is not Satisfied?

If the client does not meet the precondition then the server can do what ever it wants!

This will simplify the code.
If one does not use contracts, then often both the client and server end up check for the preconditions. The server to avoid a problem. The client in checking error codes (or exceptions) returned by the server.
Boundary conditions and preconditions add a great deal of complexity to code. They are responsible for many bugs. Work to eliminate boundary conditions and precondition.
/**
 * Requires selection is valid selection and 
 * the selection is not disabled
 */
public void execute( int selection ) throws IOException {
   //now execute the method
}

or:

/**
 * Requires selection is valid selection and 
 * the selection is not disabled
 */
public void execute( int selection ) throws IOException {
   Assert.condition( isValid( selection ));
   Assert.condition( menuOption.at( selection ).isEnabled() );
   //now execute the method
}

Listen Here!S-mar9 4mins Doc 12, Assignment 2 Comments Slide # 15

Repeating Algorithms

Example

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class IntegerQuestion {
   private static BufferedReader keyboard =
      new BufferedReader( new InputStreamReader(System.in));
      
   private String questionText;
   private int answer; 
   
   public IntegerQuestion( String question) {questionText = question; }
   
   public int answer() { return answer; }
      
   public void askQuestion() throws IOException {
      boolean haveValidAnswer = false;
      
      while (!haveValidAnswer) {
         String rawResponse = null;
         try {
            System.out.println( questionText );
            System.out.print( "Your answer: " );
            rawResponse = keyboard.readLine();
            answer = Integer.parseInt( rawResponse.trim() );
            haveValidAnswer = true;
         } catch (NumberFormatException badInput ) {
            System.out.println();
            System.out.println( rawResponse + 
               "is not a valid integer. Please try again. " );
            System.out.println();
         }
      }
   }
}

Doc 12, Assignment 2 Comments Slide # 16
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class StringQuestion {
   private static BufferedReader keyboard =
      new BufferedReader( new InputStreamReader(System.in));
      
   private String questionText;
   private String answer; 
   
   public StringQuestion( String question) {questionText = question; }
   
   public String answer() { return answer; }
      
   public void askQuestion() throws IOException {
      boolean haveValidAnswer = false;
      
      while (!haveValidAnswer) {
         String rawResponse = null;
            System.out.println( questionText );
            System.out.print( "Your answer: " );
            answer = keyboard.readLine().trim();
         if (answer.length() > 0 )
            haveValidAnswer = true;
         else {
            System.out.println();
            System.out.println( rawResponse + 
               "is not a valid integer. Please try again. " );
            System.out.println();
         }
      }
   }
}

Listen Here!S-mar9 11mins Doc 12, Assignment 2 Comments Slide # 17
is-analogous-to

If class X is-analogous-to class Y then look for superclass

When you look at IntegerQuestion and StringQuestion they are very similar. When you add the code to perform the callbacks, there is even more similarity. Another sign is that the amount of cutting and pasting of code one would do between the two classes. The stuff you cut and paste should be in a parent class.

Doc 12, Assignment 2 Comments Slide # 18

ASCIIComponent Classes


ASCIIComponent Methods

Base class for all sdsu.awt ASCII UI classes
public ASCIIComponent( String text )
public void enable()
public void disable()
public boolean isEnabled()
public void display()
public String toString() 
public Object response()
public void run() throws IOException 
Subclass override the following to provide special behavior
protected void respondToUserInput() throws IOException
protected String badInputMessage( String badInput ) 
protected String getUserResponse() throws IOException 
protected boolean validateAndStore( String userText) 

Listen Here!S-mar9 to end Doc 12, Assignment 2 Comments Slide # 19

ASCIIComponent

package sdsu.awt;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
public abstract class ASCIIComponent {
   protected static final String NEW_LINE = 
      System.getProperty( "line.separator" );
      
   protected static BufferedReader in =
      new BufferedReader( new InputStreamReader(System.in));
   protected static PrintWriter out = new PrintWriter( System.out, true );
   protected String displayText;
   protected Object userResponse;
   protected boolean isEnabled = true;
   
   public ASCIIComponent( String text ) { displayText = text; }
   
   public void enable() { isEnabled = true; }
   
   public void disable() { isEnabled = false; }
   
   public boolean isEnabled() { return isEnabled; }
         
   public void display() { out.println( toString() ); }
   
   public String toString() {
      if (!isEnabled)
         return "Disabled" ;
      else
         return displayText;
   }
      
   public Object response() { return userResponse; }
   
   public void run() throws IOException {
      if (!isEnabled)
         return;
      boolean haveValidAnswer = false;
      
      while (!haveValidAnswer) {
         display();
         String rawResponse = getUserResponse();
         haveValidAnswer = validateAndStore( rawResponse );
         if (!haveValidAnswer)
            displayBadInputMessage( rawResponse );
      }
      respondToUserInput();
   }
   
   /** Sublcass to override to provide specialized action is
    * response to user inputing valid data.
    * @exception IOException on IO problems reading users
    *      response 
    */
   protected void respondToUserInput() throws IOException { }
      
   protected void displayBadInputMessage( String badInput ) {
      out.println();
      out.println( badInputMessage ( badInput ) );
      out.println();
   }
      
   /** Sublcass to override to provide specialized message
    * when user input invalid data.
    * @param badInput text entered by user 
    */
   protected String badInputMessage( String badInput ) {
      return badInput + " is not valid input. Please try again.";
   }
      
   /** Sublcass to override to provide specialized prompting
    * of user and reading of users response.
    * @exception IOException on IO problems reading users
    *      response 
    */
   protected String getUserResponse() throws IOException {
      out.print( "Your answer: " );
      out.flush();
      return in.readLine();
   }
   
   /** Sublcass to override to provide validation of user input
    * Store validated user input in userResponse
    * Return true if input is valid
    */
   protected boolean validateAndStore( String userText) {
      this.userResponse = userText;
      return true;
   }
}

Doc 12, Assignment 2 Comments Slide # 20

IntegerQuestion

package sdsu.awt;
public class IntegerQuestion extends ASCIIComponent {
   public IntegerQuestion( String text ) {
      super( text);
   }
   protected String badInputMessage( String badInput ) {
      return badInput + " is not a valid integer. Please try again.";
   }
   protected boolean validateAndStore( String userText) {
      try {
         userResponse =  new Integer( userText.trim() );
         return true;
      }
      catch (NumberFormatException badInput ) {
         return false;
      }
   }
}

Doc 12, Assignment 2 Comments Slide # 21

BooleanQuestion

package sdsu.awt;
import java.util.List;
import java.util.Arrays;
public class BooleanQuestion extends ASCIIComponent {
   private List trueAnswers =Arrays.asList( new String[] { "true", "t", "yes", "y" });
   private List falseAnswers =Arrays.asList( new String[] { "false", "f", "no", "n" });
      
   public BooleanQuestion( String text ) { super( text); }
   protected String badInputMessage( String badInput ) {
      return badInput + " is not a valid true/false response. " +
         NEW_LINE + "Use true, t, yes, or y for positive response" +
         NEW_LINE + "Use false, f, no, or n for negative response" +
         NEW_LINE + "Please try again";
   }
   protected boolean validateAndStore( String userText) {
      userText = userText.trim().toLowerCase();
      if (trueAnswers.contains( userText ) ) {
         userResponse = new Boolean( true );
         return true;
      }
      if (falseAnswers.contains( userText ) ) {
         userResponse = new Boolean( false );
         return true;
      }
      return false;
   }
}

Doc 12, Assignment 2 Comments Slide # 22

Template Method

Intent
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses

Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure



Applicability

Use the template method

To implement the invariant parts of an algorithm once and leave it up to subclasses to implement the primitive operations to vary behavior
When common behavior among subclasses should be factored and localized in a common class to avoid code duplication
To control subclass extensions. You define a template method that calls "hook" operations at specific points, thereby permitting extensions only at those points


Doc 12, Assignment 2 Comments Slide # 23
Template methods tend to call:
concrete operations
concrete AbstractClass operations
primitive operations - must be overridden
hook operations - can be overridden

It is important to denote:
which methods must overridden
which methods can be overridden
which methods can not be overridden

Doc 12, Assignment 2 Comments Slide # 24

The Menu Part




ASCIIComposite Methods
public ASCIIComposite( String title )
public void add( ASCIIComponent aComponent ) 
public void remove( ASCIIComponent aComponent )
public void insert( ASCIIComponent aComponent, int index )
public ASCIIComponent get( int index)
public int getItemCount() 
public void enableComponent( int index )
public void disableComponent( int index )
public void display()

Doc 12, Assignment 2 Comments Slide # 25

ASCIIComposite

package sdsu.awt;
import java.util.ArrayList;
public abstract class ASCIIComposite extends ASCIIComponent {
   ArrayList components = new ArrayList();
   public ASCIIComposite( String title ) { super( title ); }
   public void add( ASCIIComponent aComponent ) { 
      components.add( aComponent );
   }
   public void remove( ASCIIComponent aComponent ) {
      components.remove( aComponent );
   }
   public void insert( ASCIIComponent aComponent, int index ) {
      components.add(index,  aComponent );
   }
   public void enableComponent( int index ) {
      ASCIIComponent toEnable = get( index);
      toEnable.enable();
   }
   public void disableComponent( int index ) {
      ASCIIComponent toDisable = get( index);
      toDisable.disable();
   }
   public void display() {
      out.println();
      if (!isEnabled)
         out.println("Disabled");
      
      out.println( "*** " + toString() + " ***" );
      for (int k = 0; k < components.size(); k++ )
         out.println( "" + (k) + ") " + get(k) );
      out.println();
   }
   public ASCIIComponent get( int index ) {
      return (ASCIIComponent) components.get( index);
   }
   public int getItemCount() { return components.size(); }
}

Doc 12, Assignment 2 Comments Slide # 26

ASCIIMenuItem

package sdsu.awt;
import java.io.IOException;
public class ASCIIMenuItem extends ASCIIComponent {
   public ASCIIMenuItem( String text) { super(text); }
   
   public void run() throws IOException { respondToUserInput(); } 
}

Doc 12, Assignment 2 Comments Slide # 27

ASCIIMenu

package sdsu.awt;
import java.util.ArrayList;
import java.io.IOException;
public class ASCIIMenu extends ASCIIComposite {
   public ASCIIMenu(String title ) { super(title ); }
      
   public void add( String menuItemText ) {
      add( new ASCIIMenuItem( menuItemText ));
   }
      
   protected String badInputMessage( String badInput ) {
      return badInput + " is not a valid integer between 0 and " +
         (getItemCount() - 1) + "." + NEW_LINE + "Please try again.";
   }
   protected boolean validateAndStore( String userText) {
      try {
         int selection =  Integer.parseInt( userText.trim() );
         if (( selection < 0 ) || ( selection >= getItemCount() ) )
            return false;
         userResponse = new Integer( selection );
         return true;
      } catch (NumberFormatException badInput ) {
         return false;
      }
   }
   protected void respondToUserInput() throws IOException {
      int selection = ((Integer) userResponse).intValue();
      ASCIIComponent selectedItem = get( selection );
      selectedItem.run();
      }
   }

Doc 12, Assignment 2 Comments Slide # 28

ASCIIDialog

package sdsu.awt;
import java.util.ArrayList;
import java.io.IOException;
public class ASCIIDialog extends ASCIIComposite {
   public ASCIIDialog(String title ) { super(title ); }
      
   public void run() throws IOException {
      if (!isEnabled)
         return;
      display();
      ArrayList responses = new ArrayList();
      for (int k = 0; k < getItemCount(); k++ ) {
         ASCIIComponent item = get( k );
         item.run();
         out.println();
         responses.add( item.response() );
      }
      userResponse = responses;
   }
}

Doc 12, Assignment 2 Comments Slide # 29

Composite Pattern



Component implements default behavior for widgets when possible

Button, Menu, etc overrides Component methods when needed

WidgetContainer will have to override widgetOperations

class WidgetContainer {
   Component[] myComponents;
   
   public void update() {
      if ( myComponents != null )
         for ( int k = 0; k < myComponents.length(); k++ )
            myComponents[k].update();
      }
   }

Doc 12, Assignment 2 Comments Slide # 30

Applicability


Use Composite pattern when

you want to represent part-whole hierarchies of objects
you want clients to be able to ignore the difference between compositions of objects and individual objects


Doc 12, Assignment 2 Comments Slide # 31
PostScript

There are still a number of problems with the ASCIIComponent classes. What additions would you make. What things would you change?

Copyright © 1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.

Previous    visitors since 08-Mar-99    Next