In this laboratory, you will build a very basic four-function calculator. This labwork is intended to give you practice with some of the concepts covered in todays lectures. In particular, it emphasizes the following topics:
This assignment is written so as to guide you through a variety of stages in the development of the calculator; the project is open-ended and you will certainly leave lab today with further ideas for the additional calculator functionality.
Before you begin work on the calculator, however, we would like you to recreate an example from today's lecture. That example is the simple self-animating echoer which reads a line from the console and writes it back.
You will need to create a .java file containing your class definition. The basic attributes of a self-animating object are:
You may want to use the java charts on program files and on classes as reference. For instructions on how to use the Java environment in lab, see the initial handout.
As in lecture, you may find the cs101.util.Console class useful. In particular, Console.in.readln() -- which returns a String -- and Console.out.println( String ) are useful functions. If you do use cs101.util.Console, you should import cs101.util.Console; or import cs101.util.*; at the top of your java file. (Otherwise you will have to refer to Console by its full name.)
Don't forget to make your class, its (no-args) constructor, and the run method public.
When you have written your java code, you can test it by running cs101.util.Main with your file as a command-line argument. For example, if your file is called Echo.java (and your class is Echo), you should run
cs101.util.Main Echo
cs101.util.Main is a utility that we've provided that simply creates an instance of your class (i.e., calls your class's constructor with no arguments).
Once you have your echoer working, you can turn to the calculator. We have provided you with a graphical user interface (GUI) for the calculator. Your mission is to write the logic controller (the "brains") behind this graphical interface. We've hidden the details of the GUI implementation, but have supplied you with a Calculator.Calculator interface (in the java sense) that describes the GUI's functionality in all of the detail that you should need.
The Calculator GUI can do such things as figuring out which button the user presses, and managing redrawing, displaying, and other various things that happen on your computer screen. However, it does not actually know how to interpret these button presses, or what answer to display. That is your object's job.
For this lab, you will need to define an active (i.e., animate) object class that will interact with the Calculator object. Although it will eventually need to be modified, you can start with the echoer you've already written. (Remember: if you renanme the class, you will also need to rename the file and vice versa.) Unlike the echoer, your calculator button handler will need a constructor that takes one parameter, the Calculator.
Make this change to your class (i.e., add the constructor parameter), but don't modify anything else in the class definition. You may want to make your new class a part of the package Calculator. (Otherwise, because the type of the constructor parameter is Calculator.Calculator, you will need to import Calculator.Calculator or import Calculator.*;) Also, because your new class's constructor takes a parameter, you'll need to run it using Calculator.Main, which passes the appropriate argument to the new instance of your class:
Calculator.Main Calculator.NewEcho
The result, when it works, should be a class that looks just like your echoer, but in addition makes a calculator GUI appear.
The previous exercise produced a calculator that echoed your console input. Now, you'll transform that into a calculator that echos your calculator button presses. To do this, you'll need to know a bit more about the Calculator.Calculator interface. You may want to look at the actual code or at the javadoc file generated from that interface. You will likely also want to read through the next few paragraphs with the code in front of you.
interface Calculator.Calculator provides four functions of interest. One tells you which button has been pressed. One tells you what's currently displayed on the calculator's screen. One allows you to write to the calculator's screen. The fourth translates between an internal representation of calculator buttons and the conventional printing representation of arithmetic operators, etc. Before you study these methods, you will need to know about that internal representation: buttonIDs.
The first things defined in Calculator.java are the "buttonID constants": Calculator.OP_ADD, Calculator.OP_SUB, Calculator.OP_MUL, Calculator.OP_DIV, Calculator.EQUALS, Calculator.DOT, and Calculator.CLEAR.
These are all static, i.e., accessible as parts of the Calculator interface: e.g., Calculator.OP_ADD. They are also final, i.e., they cannot be changed. Finally, they are all of type int.
All of these constants are distinct from one another and from the numbers 0 through 9; it is intended that the numbers 0 through 9 serve as buttonIDs for their respective buttons, although they have not been given symbolic names. (In this case, 0 through 9 are actually meaningful, not arbitrary, names!)
An additional constant, Calculator.NO_OP, is not a buttonID but is provided for convenience.
Every Calculator object has its own getButton method:
public int getButton();
The int returned is a buttonID indicating which of the buttons was pressed: one of 0 through 9, or one of the buttonIDs defined in the Calculator interface. Each time that the getButton method is called, it returns the next button pressed. So, if the calculator's user presses 3 + 4 =, your object's first call to your Calculator's getButton() method will return 3. The next call to getButton() will return Calculator.OP_ADD. The third call will return 4, and the fourth call will return Calculator.EQUALS. Then, if you call your Calculator's getButton method again, it will wait for another button to be pressed before returning.
Each Calculator object also has its own getText and setText methods for interacting with the Calculator's display. (Note that the display, and these methods, operate using Strings, not a numeric type such as int.) Their signatures are as follows:
public String getText(); public void setText( String s);
getText() returns the String currently displayed on that Calculator's screen. Unlike getButton(), getText() doesn't wait for something to happen (e.g., for the value to change) before returning it; it just tells you what text is currently being displayed.
setText replaces any currently displayed text with its single argument, a String.
getButtonLabel is a method that takes a buttonID as an argument and returns a (single-character) String suitable for labeling that button. This is particularly useful if you want to print a number or operator, e.g., on the calculator's screen.
You may also wish to familiarize yourself with the following method signatures or to refer back to them at later points in this exercise.
We've provided you with a coercion routine which takes a String and returns a floating point (double) decimal number:
double cs101.util.Coerce.StringTodouble( String s);
(Remember that in Java, double (the primitive data type) and Double (the object) are two different kinds of things.)
In fact, it is built out of two java.lang.Double methods:
public static Double Double.valueOf( String s); public double Double.doubleValue;
You can use these methods directly if you prefer.
To produce a String from a double, you can use:
public static String String.valueOf( double d);
To figure out whether a char or String appears in a given String, use
public int String.indexOf( int ch); public int String.indexOf( String str);
Now that you understand the Calculator interface, you should be able to transform your console echoer into a calculator echoer. (Of course, you can also use System.out.println, but echoing to the calculator is so much cooler!) You can start by echoing back the buttonIDs (which are ints) as they are handed to you, but you'll get prettier results if you use the getButtonLabel method, which provides you with more informative String names for the non-numeric buttons.
Once you've gotten echoing to work, you can start to build in some logic. A useful feature to have is a reset feature -- that is, a certain button or sequence of button presses that will bring your calculator back into a known state.
Write code that will allow you to reset your calculator. For this purpose, you will probably find it useful to define a reset() method which you can call whenever you want to reset the calculator. Later on, you can then fill the body of reset() with the things you want to do when you reset (i.e., setting fields to their appropriate values). Remember that there is nothing wrong with an object calling its own methods. In fact, methods used in this way can be very useful for grouping commonly used code together, so that instead of rewriting the same code whenever you need it, you can just call the method.
Question: How would you make the calculator reset (i.e., call the reset() method you have defined in your class) when then the CLEAR button is pressed, but not at any other time?
Remember, don't try to implement a complex calculator all at once. Break it down into pieces that you can test independently, and test them thoroughly before moving on to the next step.
At this point, you can now try to design a simple ButtonHandler that accepts only digit op digit =. Any other input should cause the ButtonHandler to reset itself. Map out the logic of this controller. At each step, be explicit about what appears on the Calculator's display. Since the operator does not appear on the display, you may want a place to store it. Be sure that you know where you'll keep each piece of information at each point along the way.
digit op digit = may well be as much as you have time for today. If you do have extra time -- or if you want to keep thinking about this problem after you leave -- you can think about building more complexity into your calculator in any of several ways. Take these one step at a time. Some of them require limited additional storage, e.g., if you wish to treat all digits or all numbers uniformly.
In each case, be sure that your logic can handle any button press at any time. Think of examples that your code should work on, as well as examples where it might not behave as you would like. Don't forget to build only a small addition before testing your code again. You should always begin labwork with a simple, independently testable implementation and build on it as each stage works robustly. Compile and run your code after each addition to make sure that you've built things correctly. If you have problems, it is better (and easier) to identify them early.
Congratulations! You've built your first working Java program!
This course is a part of Lynn Andrea Stein's Rethinking CS101 project at
the MIT AI Lab and the Department of Electrical Engineering and
Computer Science at the Massachusetts
Institute of Technology.
Questions or comments: