CS46A Lab

Implementing Classes

Copyright © Cay S. Horstmann, Kathleen O’Brien 2009-2014 Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

See lab2 for reporting instructions.

robot image

A. Building Our Own Robot

  1. You have used our robot class for a couple of labs. Now you'll see how to build such a class yourself. Make a project lab3 inside your cs46a/labs directory and import the graphics classes as you did in lab 2. Then right-click on the Robot class and select “Delete”. We do not want that one. We'll make our own in a minute.

    See the robot image above? Save it as myrobot.jpeg in your cs46a/labs/lab3 folder. If you do not know how to save an image from the Internet, ask your instructor. Here is what my lab3 directory looks like on my Mac. myrobot.jpeg is inside the lab3 project folder.

    project directory

    Now make a new class Robot in the lab3 project. Open it in the editor. Remove everything inside the class leaving the opening and closing braces. A robot has an x- and y-position. Add instance variables for the x-position and the y-position. Declare them as ints. Driver: Put the instance variable declarations in your report.

  2. Let's add the constructor. The constructor has the same name as the class and it has no return type. It takes the x, y coordinates as parameters.
    public Robot(int theX, int theY)
    {
          ....
    }
  3. The purpose of the constructor is to initialize the instance variables. Go ahead and do that now.
  4. We also want to see the robot. Add an instance variable
     private Picture pic;
    In the constructor, initialize it with
     pic = new Picture("myrobot.jpeg");

    Notice you do not redeclare the variable pic in the constructor but simply assign a value to it. That means you do not write Picture pic = new ...
  5. Make sure the code compiles. Driver: What is the code of the constructor?
  6. Now go to the Bluej workbench, right-click on the Robot class, and select the constructor. In the dialog box, add values for x and y. What happens? Did you see your picture? If not, why not?
  7. Remember that you need to call the draw method on a Picture object to actually display the picture. Do that in the constructor and repeat the preceding step. Now your picture should show. Driver: Exactly what did you add to the constructor?
  8. The robot has a method moveRight. Add the method header for the moveRight method.
    A method header has this form:
    access-specifier return-type method-name(parameter-list)
    • access-specifier: either public or private. It should be public in this case
    • return-type: what type of data the method returns or void if nothing is returned. This method's return type is void
    • method-name: what the method is called, in this case moveRight
    • parameter-list: the extra information the method needs to do its job. In this case there are no parameters, but you still need parentheses.
    Add a Javadoc comment to describe the purpose. The method takes no parameters and returns nothing so there is no @param or @return. Add the opening and closing braces for the method. The method contains no code right now. It is what is called a stub. We will add code later. Check that your code compiles.Driver: What is the code of the method so far (just the Javadoc, header and braces)?
  9. Now let's complete the moveRight() method to make the robot move. In the moveRight method, we want to move by pic.getWidth() pixels, so that it moves just to the right of where it was before. Simply call pic.translate with the parameters needed to make this happen. Driver: What did you put into the method?
  10. Compile, make a robot in the workbench, and call moveRight (by right-clicking on the red blob). Scribe: What happens?

Congratulations, you just programmed a robot!

B. Making the Robot Better

  1. Let's make our robot class a bit more useful. First, add a method moveDown that makes the robot move downwards by the height of the image. Driver: What is the code of the method?
  2. Pat yourself on the back if you included the Javadoc comment with the previous answer. If you didn't, add it now.
    Scribe: How did you check that your code works correctly?
    Unlike the robot from the previous lab, our robot can't turn. That's OK. It's a different kind of robot.
  3. What if you want to move by longer distances, e.g. 5 grid units to the right? (That means, 5 times the width of the picture.) Or 3 units to the left? It would be nice to have a method for that. Let's call it moveHorizontally. This method has a parameter. After you finish coding the method, you will call the method in the workbench by creating a robot1 object, right clicking and selecting moveHorizontally() and entering 5 as the parameter. Driver: Write the javadoc and the method header. What are they?
  4. I don't know what you called the parameter of your method—it might be something like n or units or steps. Whatever it is, you will need it when you implement the moveHorizontally method. Translate the picture. In the call to translate, multiply the parameter with the picture's width. Driver: What is the body of the method?
  5. Now try it out. Make a robot, move it horizontally by 5 units, then by -3 units (using the red blob on the workbench to make the calls). Scribe: Where is the robot now?
    Make sure that you multiply by pic.getWidth(). Otherwise, the robot moves only 5 pixels or -3 pixels. That's not what we want.
  6. Move it by another -3 units. Scribe: What happens?
  7. That's normal. The canvas doesn't show any pixels with negative coordinates.
    Now implement a moveVertically method in exactly the same way. Driver: What is the code?
  8. Make another robot and move it horizontally by 5, then vertically by 5. Scribe: What happens?
  9. Scribe: How do you move it back to the original location? (There is more than one correct answer.)

C. Tracking the Robot's Location

  1. We'd like to know where the robot is, not in pixels but in the number of “grid units” or steps in x- and y-direction. Add the following method:
    /**
       Gets the horizontal grid location of this robot.
       @return the horizontal grid location
    */        
    public int getX()
    {
       return x;  
    }
    Scribe: Is this method an accessor or a mutator? Why?
  2. Now make a robot, and call moveHorizontally(5). Scribe: What value do you expect to get when you call getX?
  3. Call getX (on the red blob, as before). Scribe: What value did you actually get? Why?
  4. OK, that couldn't have worked. We never updated x. Update x in the moveHorizontally method. (Hint: What do you expect x to be after calling moveHorizontally(5), then moveHorizontally(-3)? Driver: What change did you make to the method?
  5. Test it out. Make a robot, call moveHorizontally(5), then moveHorizontally(-3), then getX. What value do you get?
  6. There is another method that you need to fix to track the x location. Which method is that? (Hint: What other method changes the x coordinate?) What is the fix?
  7. Now do the same for the y value. Add an accessor and fix the appropriate mutator methods to update y Driver: Past in the code for your class.
  8. Scribe: How did you test your changes?

D. Testing

  1. In the preceding sections, you tested methods by directly executing them in the Bluej workbench environment. That's great for initial development, but as your projects get more complex, it becomes quite tedious. To be more productive, you should write tester programs. Next semester, you will learn how to use a professional testing framework, but for now, we use an approach that is much easier and also quite effective.

    Whenever you want to test something, write a program that does some work, then prints out both the actual and the expected values of a method call. By convention, the program name ends in Tester.

    For example, when you moved the robot by 5 units and then by -3 units, you expected its x position to be 2 units.

    To your Bluej project add a class MoveTester. Remove everything inside the class leaving the opening and closing braces. Add a main method: public static void main. Then in the main method do this:

    Driver: What is the complete source code of your tester class?

  2. Scribe: What happens when you run the class?
  3. Now make another test case for the vertical movement. Move by 6 units vertically, then by -4 units. Print the actual and expected y position. Driver: What lines did you add to the program?
    First print the actual and expected x value, then the actual and expected y value.
  4. What happens now when you run the class?
  5. There is a reason for making instance variables of a class private. Sometimes, we want to change or rearrange them, without affecting the public interface of the class. In our case, consider the x and y instance variables. They are a bit of a waste because the Picture object also has x and y positions. Suppose the robot moves to the right. Then x is increased, and pic.getX() is also increased. Scribe: Are they increased by the same amount? (You may want to think through what happens when you move by 1 unit.)
  6. We measure the robot's x and y in grid units, not pixels. When we move the robot n grid unit horizontally, pic.getX() becomes n * pic.getWidth() . Scribe: Therefore, how can you compute the robot's x value from pic.getX() and pic.getWidth()?Think of it as an algebra problem.
  7. Reimplement the getX() method to return pic.getX() / pic.getWidth(). Run the tester again. Scribe: What happens?
    Reimplement means to write the method in a different way.
  8. That's the value of the tester. You just changed the internals of a method, and now you can immediately verify that the change works.
    Reimplement the getY() method in the same way and re-run the tester. It should still pass. Driver: What is the code of the changed method?
  9. Note that the getX and getY methods no longer use x and y at all. Scribe: Is there any need to keep these variables, or can they be eliminated? Explain your answer.