|
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