CS 535 Object-Oriented Programming & Design Spring Semester, 1999 Assignment 2 Comments |
||
---|---|---|
© 1999, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 21-Mar-99 |
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
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.
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.
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.
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
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?
Cut & Pasting of Code
There are two major problems of cutting and pasting of code:
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 }
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; }
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
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 }
Programming By Contract
Precondition
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.
/** * Requires selection is valid selection and * the selection is not disabled */ public void execute( int selection ) throws IOException { //now execute the method }
/** * 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 }
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(); } } } }
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(); } } } }
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.
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 IOExceptionSubclass 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)
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; } }
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; } } }
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; } }
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
Template methods tend to call:
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()
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(); } }
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(); } }
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(); } }
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; } }
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(); } }
Applicability
Use Composite pattern when
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