6.096 Fall '97
Introduction to Interactive Programming

Laboratory 4: Dispatch and Procedures

http://www-cs101.ai.mit.edu/courses/fall97/psets/Calculator/

Introduction

As per policy, you are encouraged to discuss this assignment in detail with your classmates, but you are required to do the write-up on your own. You may get comments from other students on all portions of your write-up before turning it in, if you wish. Please include the names of anyone with whom you collaborate, in any way, on this assignment, and indicate the nature of the collaboration. [Failure to include this information is a violation of the collaboration policy.]

This assignment emphasizes the following topics:

You should read through this entire assignment and complete the Lab preparation section before coming to lab. Some portions of the Post Lab write-up also require your thinking about them before and during the laboratory portion of the assignment.

This assignment is due at the beginning of class on Wednesday, 15 October.


Contents


Pre-Lab

Although most of the work on the pre-lab assignment may be done in cooperation with other students, we recommend that you design at least part of one of the FSMs by yourself. In any case, make sure that each of you fully understands the solutions that you come up with.

As always, a neat write-up of the finger exercises should be turned in along with your completed laboratory.

A. Finger exercises

This week's finger exercises are preparation for the in-class examination on Monday. Begin by reading over the quiz handout. Be sure that you understand the Negative Multiple Choice format.

The second part of your finger exercises is to put together a crib sheet for the exam. You may write anything you want on either side of a single 8.5" x 11" piece of paper. You may work with other students to discuss what might helpfully go on that piece of paper, but you should write your paper up yourself.

Please save your exam crib sheet to turn in with your problem set.

B. Lab Preparation

The laboratory project for this week is a simple four-function calculator. We have constructed the calculator's GUI (graphical user interface), and your mission is to write the logic controller (the "brains") behind this graphical interface.

Specifically, you will implement an active object class which will interact with a Calculator object to respond to the user. The Calculator (see attached interface documentation) 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. A particular instance of a Calculator object will be passed to your object's constructor when your object is created (i.e., when you run the Calculator.Main program), so be sure to define an appropriate constructor. (Note that this is very similar to what you did in last week's Balancer lab.) Your object must then tell the Calculator exactly what to do, such as recognizing and appropriately responding to the user's button presses.

Running the Demo(optional)

You do not need to run the week's program before coming to lab, since you surely know what a calculator does. However, a simple implementation is available on Athena, which you can run by typing the following on an Athena Sun workstation (assuming that you have successfully set up your account to run java. E-mail the course staff for help.)
athena %  java Calculator.Main

Reviewing the Calculator Interface

You should begin by examining the Calculator interface (attached). (You may also want to take a look at the javadoc file generated from that interface, also attached.) It defines everything that you'll need to know about a Calculator. In particular, it contains three kinds of declarations:

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. (Hint: think switch/case.)

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.

An additional constant, Calculator.NO_OP, is not a buttonID but is provided for convenience.

ButtonLabels

Calculator.ButtonLabels is a constant array of Strings that provides names for each button. That is, Calculator.ButtonLabels[buttonID] is a String containing a suitable label for the button identified with the int buttonID (which can be any of the defined buttonID constants, or 0-9). All of the buttonIDs, as well as 0 through 9, may be used as indices for ButtonLabels.

Methods: getText, setText, getButton

Every Calculator object has its own getButton method: 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 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.

Reviewing the provided helper methods

Before you go to lab, you should also familiarize yourself with the following method signatures.

We've provided you with a coercion routine which takes a String and returns a floating point (double) decimal number:

(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:

You can use these methods directly if you prefer.

To produce a String from a double, you can use:

To figure out whether a char or String appears in a given String, use

Designing the FSM controller(s)

In lab, you will be writing a class that will interact with an instance of the Calculator object, and serve as a controller for it. You should name your class ButtonHandler, and it should be a self-animating active object (much like the class you created for the Balancer lab). At each point in time, your class should ask the Calculator for the next button pressed, and then, if appropriate, perform any desired computations and update the Calculator's display. (Needless to say, it should also update its own internal state appropriately at each step.)

As usual, you should follow an incremental design strategy -- keeping the end goal (a working calculator) in mind, but starting with simple implementations first. Before going to lab, think about how you would implement the following:

Implementing Echo

A simple test that will help ensure that your infrastructure is working is to simply echo each button pressed back to its Calculator's screen. (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 ButtonLabels array, which provides you with more informative String names for the non-numeric buttons. (Remember that ButtonLabels belongs to the Calculator interface, not to an individual instance object.)

Implementing Reset

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?

Scaling Up

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.

Questions: Draw an FSM state diagram that represents this logic. How many states does it have? How do you move from one state to another?

Once you have digit op digit =, you can build more complexity 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. Write these examples down and experiment with them in lab.

Drafting your code

As always, you should draft your code before coming to lab.

Your code should define a class called ButtonHandler. Its constructor method should take a Calculator as its only argument. Your ButtonHandler should be an active object: it should have its own (activated) animacy. At each point in time, it should gobble up a button press (by calling the Calculator object's getButton() method) and, if appropriate, update the Calculator's display. (It should also update its own internal state at each step.)

You will almost certainly want to keep your central control loop small. This is best accomplished by spinning off procedures to handle separate cases. By passing parameters, you can make these cases reasonably general. Remember that you don't want to be writing a lot of the same code many times. When this happens, it is a good hint that you've got a common pattern, and it's always a good idea to write down the common pattern, give it a name, and make it into its own method. But make sure that your common pattern makes sense, too. A good rule of thumb is: if you can't explain your code pretty clearly and concisely to someone else, it isn't designed right.

Details of the ButtonHandler specification, as well as hints for a stage-by-stage implementation, are given in the Laboratory section, below. Roughly speaking, it follows these steps:

  1. Implement ButtonHandler as an active object type, but with minimal logic.
  2. Build a simple FSM into your code.
  3. Expand your code in pieces, one step at a time, working with and not against your abstractions.

Laboratory

What to Bring to Lab

You need to bring your finger exercises, finite state machines, and code draft with you to lab, and you will not be checked in without them. You should also have read the entire problem set and be prepared to discuss an implementation strategy before you arrive.

Getting Started

Specific directions on how to set up and run your code on Java Workshop will be provided on a handout in lab. It should be very similar to what you did for the Balancer lab.

When you begin to code, even though you may have constructed a sophisticated design for your ButtonHandler, you should always begin your labwork with a simple, independently testable implementation and build on it as each stage works robustly.

For example, start with a very simple class with just a constructor that takes a Calculator as its only argument, and make sure that it compiles. Then, you can begin testing your class design, without any of the internal logic. Although it makes a pretty lousy calculator, a simple test that will help ensure that your infrastructure is working is to simply echo each button pressed back to its Calculator's screen.

Compile and run your code to make sure that you've built things correctly. If you have problems, now is a good time to work them out.

Scaling up

Once you can get a very simple class working, you can then move on to implementing a real calculator. Start with the 1-digit calculator you were asked to design in the pre-lab, and add functionality incrementally if you have the time.

Remember the following, quoted from the Evaluation Policies handout: "... you will in general get a higher score for basic but elegant, well-tested, well-documented, and well-understood code than for adding features to poorly written/documented/tested code. We are more interested in how well you do what you do than in how much you do."

Record your experiences, including tests that various stages of your implementation succeed and fail. Make sure that you understand how your code behaves, and how it will behave under inappropriate as well as appropriate circumstances.

Before you leave

Before you leave lab, you will need to have your code checked off by a course staff member.


Post-Lab, AKA What To Turn In

Your completed assignment should include: This lab assignment is due on Wednesday, 15 October, at the beginning of class. It may, of course, be turned in earlier.


Solutions

Solutions will be available via the web at the conclusion of the lab.


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:

<cs101-webmaster@ai.mit.edu>
Last modified: Wed Oct 1 01:20:27 1997