Designing with Abstract Classes and Interfaces
an Example in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
import java.util.NoSuchElementException; import java.util.StringTokenizer; /** * A 5-function calculator that can evaluate expressions * consisting of an integer operand followed by an operator * followed by an integer operand (e.g.,5+3) * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class IntegerCalculator { private String operators; /** * Default Constructor */ public IntegerCalculator() { operators = new String("%/x-+"); } /** * Evaluate an expression * * @param expression The String containing the expression * @return The result of evaluating the expression * @throws ArithmeticException If an arithmetic problem arises * @throws NoSuchElementException If an operand or operator is missing * @throws NumberFormatException If an operand is not an int */ public int calculate(String expression) throws ArithmeticException, NoSuchElementException, NumberFormatException { int left, result, right; String leftS, operator, rightS; StringTokenizer st; // Construct a StringTokenizer that uses the operators // as delimiters and returns the delimiters as // tokens st = new StringTokenizer(expression, operators, true); // Parse the expression leftS = st.nextToken(); operator = st.nextToken(); rightS = st.nextToken(); // Create the left side operand left = 0; // Initialize the left side operand left = Integer.parseInt(leftS); // Create the right side operand right = 0; // Initialize the right side operand right = Integer.parseInt(rightS); // Perform the operation if (operator.equals("%")) result = left % right; else if (operator.equals("/")) result = left / right; else if (operator.equals("x")) result = left * right; else if (operator.equals("-")) result = left - right; else result = left + right; // "+" // Return the result return result; } }
String
representations
String
representations
-- Initializeable
Increaseable
/** * Requirements of Initializeable objects (i.e., objects that can be * initialized from a String representation) * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public interface Initializeable { /** * Parse a String representation of this Object. * * @param s The String representation */ public abstract void fromString(String s); }
/** * Requirements of Increaseable objects (i.e., mutable objects that * can be added to) * * @author Prof. David Bernstein, James Madison University * @version 0.1 */ public interface Increaseable { /** * Increase this Object by the given Object * * @param other The other Object */ public abstract void increaseBy(Increaseable other); }
/** * Requirements of an Operand. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public interface Operand extends Initializeable, Increaseable { }
Initializeable
in Weight
/** * Initialize the attributes of this Weight from a String representation * (required by Initializeable). * * @param s The String representation (delimited by commas) */ public void fromString(String s) { int ounces, pounds; String token; StringTokenizer st; st = new StringTokenizer(s, ","); try { token = st.nextToken(); pounds = Integer.parseInt(token); token = st.nextToken(); ounces = Integer.parseInt(token); value = Math.abs(ounces) + Math.abs(pounds) * 16; } catch (NoSuchElementException nsee) { // Leave the value as it is } catch (NumberFormatException nfe) { // Leave the values as it is } }
Initializeable
in Fraction
/** * Initialize tha attributes of this object from a String representation * (required by Initializeable). * * @param s The String representation (delimited by a slash) */ public void fromString(String s) { String token; StringTokenizer st; st = new StringTokenizer(s, "/"); try { token = st.nextToken(); numerator = Integer.parseInt(token); token = st.nextToken(); denominator = Integer.parseInt(token); } catch (NoSuchElementException nsee) { // Leave the remaining default values as they are } catch (NumberFormatException nfe) { // Leave the remaining default values as they are } }
Increaseable
in Weight
/** * Increase this Weight by a given amount (required by Increaseable) * * @param other The amount to change this Weight by */ public void increaseBy(Increaseable otherWeight) { Weight other; // This typecast is dangerous. We will talk about how // to elimninate the need for it later. other = (Weight)otherWeight; value += other.value; }
Increaseable
in Fraction
/** * Increase this Fraction by a given amount (required by Increaseable) * * @param otherFraction The amount to change this Fraction by */ public void increaseBy(Increaseable otherFraction) { Fraction other; int cd; // This typecast is dangerous. We will talk about how // to eliminate the need for it later. other = (Fraction)otherFraction; cd = commonDenominator(other); if (denominator == cd) { numerator += numerator; } else { numerator = numerator *(cd/denominator) + other.numerator*(cd/other.denominator); denominator = cd; } }
WeightAddingMachine
Classimport java.util.*; /** * A bad implementation of a WeightAddingMachine. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class WeightAddingMachineBad { /** * Evaluate an expression. * * @param expression A String representation of an expression * @return The result of evaluating the expression */ public String evaluate(String expression) { Operand left, right; String leftToken, operator, rightToken; StringTokenizer st; // Construct a StringTokenizer that uses the operator(s) // as delimiters st = new StringTokenizer(expression, "+"); // Tokenize the expression (which, in the current version, must // involve the + operator leftToken = st.nextToken(); rightToken = st.nextToken(); // Create the left side operand left = new Weight(); // Initialize the left side operand left.fromString(leftToken); // Create the right side operand right = new Weight(); // Initialize the right side operand right.fromString(rightToken); // Perform the operation left.increaseBy(right); // Return the result return left.toString(); } }
FractionAddingMachine
Classimport java.util.*; /** * A bad implementation of a FractionAddingMachine. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class FractionAddingMachineBad { /** * Evaluate an expression. * * @param expression A String representation of an expression * @return The result of evaluating the expression */ public String evaluate(String expression) { Operand left, right; String leftToken, operator, rightToken; StringTokenizer st; // Construct a StringTokenizer that uses the operator(s) // as delimiters st = new StringTokenizer(expression, "+"); // Tokenize the expression (which, in the current version, must // involve the + operator leftToken = st.nextToken(); rightToken = st.nextToken(); // Create the left side operand left = new Fraction(); // Initialize the left side operand left.fromString(leftToken); // Create the right side operand right = new Fraction(); // Initialize the right side operand right.fromString(rightToken); // Perform the operation left.increaseBy(right); // Return the result return left.toString(); } }
Operand
objectsOperand
objectsAddingMachine
Classimport java.util.*; /** * A partial implementation of an adding machine. * * This implementation can add any two objects that implement * both the Increaseable and Initializeable interfaces * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public abstract class AddingMachine { /** * Evaluate an expression. * * @param expression A String representation of an expression * @return The result of evaluating the expression */ public String evaluate(String expression) { Operand left, right; String leftToken, operator, rightToken; StringTokenizer st; // Construct a StringTokenizer that uses the operator(s) // as delimiters st = new StringTokenizer(expression, "+"); // Tokenize the expression (which, in the current version, must // involve the + operator leftToken = st.nextToken(); rightToken = st.nextToken(); // Create the left side operand left = createOperand(); // Initialize the left side operand left.fromString(leftToken); // Create the right side operand right = createOperand(); // Initialize the right side operand right.fromString(rightToken); // Perform the operation left.increaseBy(right); // Return the result return left.toString(); } /** * Create an "empty" Operand * * @return The operand (which must implement the Initializeable interface) */ protected abstract Operand createOperand(); }
WeightAddingMachine
Class/** * A partial implementation of an AddingMachine * that can operate on Weight objects * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class WeightAddingMachine extends AddingMachine { /** * Create an "empty" operand * * @return The operand */ protected Operand createOperand() { return new Weight(); } }
FractionAddingMachine
Class/** * A partial implementation of an AddingMachine * that can operate on Fraction objects * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class FractionAddingMachine extends AddingMachine { /** * Create an "empty" operand * * @return The operand */ protected Operand createOperand() { return new Fraction(); } }
import java.io.*; import java.util.*; /** * A small example of polymorphism as it arises through the * use of interfaces * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class Driver { /** * The entry point * * @param args The command-line arguments */ public static void main(String[] args) throws IOException { BufferedReader in; FractionAddingMachine fam; String line, result; WeightAddingMachine wam; in = new BufferedReader(new InputStreamReader(System.in)); // A Weight example System.out.println("Enter an expression involving Weight objects: "); line = in.readLine(); wam = new WeightAddingMachine(); result = wam.evaluate(line); System.out.println(result); // A Fraction example System.out.println("Enter an expression involving Fraction objects: "); line = in.readLine(); fam = new FractionAddingMachine(); result = fam.evaluate(line); System.out.println(result); } }
Weight
object can take many forms
as a result of the fact that it implements several
interfaces (Increaseable
,
Initializeable
) and as a result of the fact that
it implicitly extends Object
(e.g., the
toString()
method)
Fraction
object can take many forms
as a result of the fact that it implements several
interfaces (Increaseable
,
Initializeable
) and as a result of the fact that
it implicitly extends Object
(e.g., the
toString()
method)
AddingMachine
can operate on any
object that is both Initializeable
and
Increaseable