Return to lecture notes index

15-100 Lecture 12 (Monday, October 2, 2006)

Calculator Interface

Last week we wrote a simple interface called CalculatorInterface. The interface specification lists all of the behaviors that implementing classes of objects can exhibit, how to ask for each behavior, and what will be returned when each has been completed. Our calculator interface looked like this:

  interface CalculatorInterface {
    public double setNumber(double number);
    public double requestOperation(char operation);
    public double getValue();
  }
  

This means our class has to implement at least these three methods.

SimpleCalculator Class

Because we will be implementing CalculatorInterface, our skeleton should start out containing at least those methods. A constructor and instance variables are also required.

  class SimpleCalculator implements CalculatorInterface {
    private double accumulator;
    private double input;
    private char operation;
    
    public SimpleCalculator(){
      accumulator = 0.0;
      input = 0.0;
      operation = '=';
    }

    public double setNumber(double number){
    }

    public double requestOperation(char operation){
    }

    public double getValue(){
    }

  }
  

We can implement additional methods, but these methods must be written. Notice that when we first initialize our SimpleCalculator class, accumulator and input are equal to 0.0 and operation is set to equals.

Let's take a look at setNumber. This method accepts a double and should do one of two things. First, if the operation is set to equals, then we've either just created the calculator, or we've already completed our previous operation, so we should set the accumulator equal to number. If operation is set equal to anything else, however, we should set input equal to number. Basically our first number is set equal to the accumulator and then depending on what the operation is, accumulator will be modified based on the input. So if accumulator is equal to 5, input equal to 2 and we requestOperation(+), the accumulator should become 7. This calculator will not print out the result until we requestOperation(=). Let's see how se can code this.

First off, setNumber(double number):

  public double setNumber (double number) {
    if (operation == '=')
      accumulator = number;
    else
      input = number;
  
    return number;
  }
  

Now let's take a look at the getValue(). All we really want the getValue to do is return the accumulator, so:

  public double getValue() {
    return accumulator;
  }
  

Now the easy ones are done, let's think about what requestOperation(char operation) needs to do. If we pass in '=', it should compute some value. Otherwise, we just want to set the class operation equal to operation. If we think about that for a minute, however, we notice that these variables are both named operation. If we just type operation, which one does it refer to? If we just type in operation inside the request Operation method, it will refer to the one passed in through the arguments. What if we want to refer to the class/instance variable? Simply enough, we just put a this. infront of it. This refers to the "global" variable, whereas simply operation refers to the "local" variable. So now let's code out our request operation a little.

  public double requestOperation (char operation) {
    
    if (operation == '=') {
      //Compute some value
    }
    
    this.operation = operation;
      
    return accumulator;
  }
  

Now we just need to figure out what to do where the comment is. It seems like it could end up being a little indepth, so let's go ahead and write a private helper method to do it for us called compute. Keep in mind, however, that we haven't actually performed the operation yet, so we don't want to set operation to equals just yet, first we need to perform compute().

Compute() should make a decision based on the operation and modify the accumulator accordingly based on the input. Let's just look at the '+' and '-' cases first. Since we will end up having a few different if statements here, let's go ahead and make it a switch.

  private void compute() {
    
      switch (operation) { // instance variable
      case '+': accumulator += input; //accumulator = accumulator + input;
           break;
      case '-': accumulator -= input; //accumulator = accumulator - input;
           break;
      }
      
    }
  

So now if the operation is a '+', we take the accumulator and add the input to it, similarly when it is '-', we subtract input from accumulator. We can now easily implement the other operation cases:

  private void compute() {
    
      switch (operation) { // instance variable
      case '+': accumulator += input; //accumulator = accumulator + input;
           break;
      case '-': accumulator -= input;
           break;
      case '*': accumulator *= input;
           break;
      case '/': accumulator /= input;
           break;
       case '=': /* do nothing */ 
           break; 
      }
      
    }
  

Notice that we didn't put anything into the equals case, this is because we don't want to modify the accumulator if we already had the operation equals in our calculator. Why include it then? Just to be thorough really. This way when someone reads our code, including ourselves, we know that we didn't just forget about the case '=' but thought through it and decided no action was necessary.

Going back to requestOperation, we need to call on our compute method in order to actually calculate the value. Up to date our calculator now looks like this:

  class SimpleCalculator implements CalculatorInterface {
  private double accumulator;
  private double input;
  private char operation;


  public SimpleCalculator() {
    accumulator = 0.0;
    input = 0.0;
    operation = '=';
  }


  public double setNumber (double number) {
    if (operation == '=')
      this.accumulator = number;
    else
      input = number;
  
    return number;
  }
  
  
  public double getValue() {
    return accumulator;
  }
  
  
  public double requestOperation (char operation) {
    
    if (operation == '=') {
      compute();
    }
    
    this.operation = operation;
      
    return accumulator;
  }
    
    
    private void compute() {
    
      switch (operation) { // instance variable
      case '+': accumulator += input; //accumulator = accumulator + input;
           break;
      case '-': accumulator -= input;
           break;
      case '*': accumulator *= input;
           break;
      case '/': accumulator /= input;
           break;
      case '=': /* do nothing */
           break; 
      }
      
    }
  
  }
  

All that's left is to test it out a little so let's write a simple main method, create a new SimpleCalculator, call on our methods and print out the result:

  public static void main (String[] args) {
   
     SimpleCalculator sc = new SimpleCalculator();
     
     sc.setNumber (10);
     sc.requestOperation('+');
     sc.setNumber (5);
     System.out.println (sc.requestOperation('='));
   
  }
  

This will print out 15. We will go over how we created the SimpleCalculator and called on its methods inside the main method later, but notice that it looks a good deal like how we implemented Scanner.