JMU
Designing with Abstract Classes and Interfaces
an Example in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


A Review of Abstract Classes
A Review of Interfaces
Design Issues
An Example
Getting Started

A Simple Integer Calculator

javaexamples/oopbasics/calculator/IntegerCalculator.java
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;
    }

}
        
Getting Started (cont.)

Steps in the Process

  1. Tokenize the expression
  2. Create objects for the operands
  3. Initialize the operands from String representations
  4. Perform the operation (from here on, addition only)

Requirments of Operands

The Interfaces
javaexamples/oopbasics/calculator/Initializeable.java
/**
 * 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);
    
}
        
javaexamples/oopbasics/calculator/Increaseable.java
/**
 * 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);    
}
        
javaexamples/oopbasics/calculator/Operand.java
/**
 * Requirements of an Operand.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public interface Operand extends Initializeable, Increaseable
{
}
        
Implementing Initializeable in Weight
javaexamples/oopbasics/calculator/Weight.java (Fragment: Initializeable)
    /**
     * 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
       }
    }
        
Implementing Initializeable in Fraction
javaexamples/oopbasics/calculator/Fraction.java (Fragment: Initializeable)
    /**
     * 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
       }
    }

        
Implementing Increaseable in Weight
javaexamples/oopbasics/calculator/Weight.java (Fragment: Increaseable)
    /**
     * 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;
    }
        
Implementing Increaseable in Fraction
javaexamples/oopbasics/calculator/Fraction.java (Fragment: Increaseable)
    /**
     * 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;          
       }
    }
    
        
A WeightAddingMachine Class
javaexamples/oopbasics/calculator/WeightAddingMachineBad.java
import 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();       
    }
}
        
A FractionAddingMachine Class
javaexamples/oopbasics/calculator/FractionAddingMachineBad.java
import 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();       
    }
}
        
The Need for an Abstract Class
An Abstract AddingMachine Class
javaexamples/oopbasics/calculator/AddingMachine.java
import 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();
}
        
A Concrete WeightAddingMachine Class
javaexamples/oopbasics/calculator/WeightAddingMachine.java
/**
 * 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();
    }
    
}
        
A Concrete FractionAddingMachine Class
javaexamples/oopbasics/calculator/FractionAddingMachine.java
/**
 * 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();
    }
    
}
        
A Driver
javaexamples/oopbasics/calculator/Driver.java
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);
    }
    
}
        
Concluding Observations