CSE131 Module 5: Modular Design


Object-oriented languages support the design of software systems as collections of interacting objects. Each object can contain information and provide methods to access and operate on that information. After completing this module, you should...

Practice Problems

Expect exercises like these on the quiz. There will be a help session covering these exercises on Wednesday at 10:00am in the Steinberg 105 lecture hall. You will benefit most by doing the exercises before you look at the solutions or attend the Wednesday help session when they will be discussed.

Directions: Suppose you are given three classes, Widget, Machine, and Truck. Widgets have a color and a weight, and also are sometimes defective.

Machines can transform one widget into another to do things to them, like change their weight. Some machines can make a widget become defective, or can fix a defective widget so it is no longer defective. Sometimes machines are broken.

Trucks can be loaded with widgets, one at a time. Widgets are loaded and unloaded at the back of the truck, so the widget that gets loaded first will be the last widget to be unloaded. For example, suppose you load widgets a, b, and c into a truck. If you then unload them, they will come out in the order c, b, and a. If a truck is full, you can't load any more widgets into it. If a truck is empty, you can't unload any widgets from it.

Widget Machine Truck
  • Widget()
  • boolean isDefective()
  • double getWeight()
  • Machine()
  • Widget transform(Widget w)
  • boolean isBroken()
  • Truck()
  • void load(Widget w)
  • Widget unload()
  • boolean isEmpty()
  • boolean isFull()
  • double getTotalWidgetWeight()
  • Using the methods and constructors summarized above, provide a Java code fragment to perform each of the following tasks.

    1. Unload a single widget from truck t1, let machine m transform it, and put the resulting widget into truck t2.
    2. Unload a single widget from truck t1. If the widget is defective, let machine m transform it, and put the resulting widget into truck t2. Otherwise, just put the widget directly into truck t2.
    3. Unload widgets from truck t1. As you do so, put them into truck t2 unless t2 is full. Any widgets that aren't put into t2 should remain in t1. (Use a loop.)
    4. Unload all the widgets from truck t1. As you do so, put them into truck t2 unless adding a widget would make the total weight of the items in t2 exceed 1000. (Assume that t2 is initially empty.)
    5. Put widget w through machines m1, m2, and m3 in that order. (Think of this as function composition. You can write this as a single line of code. Be careful about the order of execution.)
    6. Unload all the widgets from truck t1, and sort them into three different trucks. All defective widgets should go into truck defectiveWidgets. Non-defective widgets that weigh less than 50 should go into truck lightWidgets. All remaining widgets go into truck heavyWidgets. Assume there is enough room in all of the trucks.
    7. Create 100 widgets, and put each one into a different new truck.
    8. Reverse the order of the widgets in truck t1. In otherwords, after you are finished, t1 will contain the same widgets as it did at the start, but the items will be loaded into the truck in the reverse order. (Hint: You may need some extra trucks, and you may want to write a short method to help simplify your code. Assume that any new trucks you create are sufficiently large.)
    9. Repeately put widget w through machines m1 and m2 until either the widget is defective or you are about to put w into a defective machine.
    10. Machine m can fix defective widgets, but sometimes a widget must be transformed multiple times by m before it is no longer defective. Unload all the widgets from truck t1. As you do so, put them into truck t2, but first let m fix any defective widgets. Assume t2 is sufficiently large.

      Note that this week's quiz will have two parts. The first part is based on the first ten practice problems above. The second part counts as an optional extension on the topic of test-driven development, as described in Lab 4. You will be asked to show the stages in test-driven development that might arise in the development of a particular method, similar to the following practice problem. Each part of the quiz will be graded separately. A grade of 85% or higher on the test-driven development portion will give you credit for the optional extension.

    11. Complete the following table showing the stages of development of a method using test-driven development. In each row, the developer's solution should be the simplest general solution that fits the test cases.

      public void testFoo() {
         Widget w = new Widget();
         assertEquals(0, w.foo(7));
      public void testFoo() {
         Widget w = new Widget();
         assertEquals(0, w.foo(7));
         assertEquals(7, w.foo(3));
      public class Widget {
         int v = 0;
         public int foo(int n) {
            int old = v;
            v = Math.max(v,n);
            return old;


    Download lab5.zip and import it into your CSE131 project in Eclipse.

    After you download the provided files, read the comments and code in GraphicsExample.java, which illustrates how to create graphics objects and put them on a panel in YOPS. Feel free to modify the GraphicsExample code in various ways to experiment with what happens. For example, try adding another shape. See the YOPS documentation for the various graphics shapes available and the methods they provide.

    Project: Roving Eyes

    You will implement an program with the following features.
    1. Each eyeball consists of two circles drawn on the screen, the pupil (which should be filled black) and the outline (which should be a black circle that is filled white). The pupil's radius should be about 1/4 that of the outline. However, once you have everything working, feel free to change the colors and sizes.
    2. The pupil must always be entirely within its eyeball's outline.
    3. When the user presses the mouse button, the tool creates an eyeball centered at the mouse location.
    4. As the user drags the mouse, the newly created eyeball moves around with the mouse so the user can position it.
    5. When the user releases the mouse button, the newly created eyeball just stays where it is.
    6. In addition, as the mouse cursor moves around the screen, all pupils move as close as possible to the cursor, while remaining entirely within their eyeball outlines. This will create the effect of eyes "looking at" the cursor as it moves around. (The effect is best if you place the eyeballs over the eyes on a portrait image. You may want to change the sizes of the outline and pupil to match the image you are working with.)
    Reacting to mouse events: Before you begin the implementation, it is important to understand that when you write a tool for YOPS, you can provide methods that react to the user's mouse events. In particular, a tool can provide methods called mousePressed, mouseReleased, mouseMoved, and mouseDragged. Each of these methods must be public, have a void return type, and take 3 parameters: the GraphicsPanel in which the event occured, and the integer x and y coordinates of the pixel location where the mouse event occurred within that panel. For example, to react to a mouse press, you would write a method of the form
    public void mousePressed(GraphicsPanel panel, int x, int y) {
       // In here, you would put whatever computation needs to be performed
       // when the mouse was pressed in the given panel
       // at the given (x,y) pixel location.
    For further details, see the YOPS documentation.


    1. Open the provided file Eyeball.java for editing. Fill in your name and other information in the header comment. You need to figure out the details, but here's an overview of what you need to do.

      The Eyeball constructor should take three parameters: a GraphicsPanel and x and y integer coordinates. It should create the graphics components of the eyeball (outline and pupil), centered at the given location on the panel. The constructor should also add the eyeball as a listener for mouse events on the panel, as done in the GraphicsDemo constructor.

      Create a moveTo method that takes as parameters x and y coordinates. It should move the eyeball and the pupil so that they are both centered at the given location. (The Ellipse class has a setCenter method you can use.)

      Create a lookAt method to make the pupil look toward the given point. That is, it should move the pupil as close as possible to that point without letting the pupil go outside the outline of the eyeball. (See hints below.)

      Write a mouseMoved method that will call lookAt, passing as parameter values the coordinates of the mouse cursor. That way, whenever the user moves the mouse, the eyeball will look toward the mouse. When using YOPS, remember that the mouse methods always take three parameters: a GraphicsPanel and the x and y integer coordinates. (You can see the provided GraphicsDemo.java class for an example of a mouseMoved method.)

      The toString method should return a textual description of the eyeball, including its size and center location. You may want to use this for testing purposes.

    2. Open the EyeballTool.java file. Add a mousePressed method, so that each time the user presses the mouse button, your Eyeball constructor is called to instantiate an eyeball at the specified location. Test this by running Lab5.java and selecting the eyeball tool from the list at the left of the YOPS window. Do the eyeballs look at the mouse correctly?

    3. Now modify your program so that after the user presses (but does not release) the mouse button to create an eyeball, the user can continue to hold down the mouse button and "drag" the newly created eyeball into the desired position. You'll write a mouseDragged method for this.
    Implementation hints:

    Optional Extension 1: Timed Animation

    For this extension, modify your Eyeball so that the pupil motion does not immediately move to look at the mouse cursor, but instead gradually moves toward the mouse cursor for a more creepy effect. In other words, if you move the cursor quickly from point A to point B, the eyeballs will slowly move to look at point B, stopping very briefly to look at points along the imaginary line between A and B. More specifically, each eyeball will have a point that it is currently looking at, and will also have a point that it is moving towards. Whenever the mouse moves, the point that the eyeball is moving towards will change.

    To accomplish this task, you will need to use a timer from the java swing package. Each eyeball will listen to its timer, and shift its focus gradually toward point B at each tick. (If it's already looking at point B, then it will ignore the timer.)

    To get started, you can declare an instance variable of type Timer, and initialize it in your Eyeball constructor by calling the Timer constructor that takes two parameters. The first parameter is the number of milliseconds between ticks of the timer. (50 milliseconds per tick would provide about 20 animation frames per second.) The second parameter is the listener, which will be this so that the Eyeball will be notified of each tick of the timer. Be sure to .start() the timer so it will tick and call your listener. Take a look at the lab0 Robot code to see how this is done.

    Notice that the Eyeball class is already declared to be of type ActionListener. This implies that it has an actionPerformed method. Since you have added the eyeball as a listener to the timer, at each "tick" of the timer, the eyeball's actionPerformed method will be called. Fill in the actionPerformed method body to move the pupil appropriately at each tick. It's important that you not move the pupil all at once to look at point B, but instead you will just shift the pupil's focus a little bit toward point B each time the actionPerformed method is called. You don't need to do anything with the parameter that is passed to the actionPerformed method.

    For an example use of a timer for animating motion, you can look in the Robot.java class of the lab0 package. However, be aware that the Robot is more complicated due to the painting of the line that trails behind it, and the fact that there are two types of robot motion: moving forward and turning.

    Optional Extension 2: A Cloning Tool

    The purpose of a cloning tool is to "paint" a section of an image onto another area of the same image or a different image. Implement a cloning tool that works as follows.

    1. The user selects the cloning tool from the list of buttons at the left edge of the YOPS window.
    2. The user clicks on a pixel in the image. (When the user releases the mouse button, your tool will remember which pixel location this was.) We will call this pixel the source location.
    3. Then the user will move the mouse somewhere else, press down the mouse button at the destination location, and begin dragging the mouse around in the area around that destination. As they do so, the pixels in the area around the source location will be copied to where the mouse is being dragged. Copying happens continuously. That is, as long as the user holds down the mouse button, copying occurs at every pixel location they drag to. All copying is done relative to the source location and the destination location. For example, if they drag to the area three pixels above the destination location, then the cloning tool will copy to that location the pixel that is three pixels above the source location. (You will use your Vector and Point classes from Lab 4 to figure out which pixels should be copied to the current mouse position.)
    For example, suppose the user loads a family photograph into YOPS and selects your cloning tool. If they release the mouse on someone's nose, and then beging dragging slowly in the area around someone else's nose, they will copy the first person's face on top of the second person's face.

    Take the following details into account as you design and implement your cloning tool.

    Copying within an image: Users would find it tedious to have to drag the mouse over every single pixel in the destination region. Therefore, your cloning tool should copy a small rectangular area of image pixels each time the mouse is dragged. (This will give the user the feeling that they are using a "brush.") To learn how to do this, read the YOPS documentation about Image objects, and specifically copying regions of an image.

    Getting the image from a panel: The mouse event methods have as their first parameter the GraphicsPanel in which the event occurred. However, your cloning tool will need to work with the image contained within that panel. If gp is a GraphicsPanel object, then gp.getMainImage() will return the Image object from that GraphicsPanel.


    1. Read the above specification and background carefully. Then open the CloningTool.java file in the lab5 package in Eclipse.
    2. Think about what information your cloning tool will need to use, and create instance variables to hold that information. You might declare two Point variables to hold the sourceLocation and the destinationLocation, and also declare a Vector variable to hold the difference between the source and destination locations.
    3. Write a constructor that initializes the source location to (0,0), just in case the user starts dragging before they ever release the mouse button to set a source location.
    4. Implement the mouseReleased, mousePressed, and mouseDragged methods to satisfy the above specification. Use a "brush size" of 10x10 pixels. (For some recommendations on how to implement these methods, see the hints below.)
    5. Test your program. To run it, right click on the "Lab5.java" file and select "Run -> Java Application." Does the cloning tool work as specified? (Probably not the first try.) Try to diagnose problems based on the information available. If you get a "NullPointerException," this usually means that a variable was not initialized. In such cases, always look at the stack trace on the console and find the line in the file where it occured, and check that all the variables used on that line were properly initialized.
    6. If you move the brush so that you would be copying pixels from an area outside the image, you will get an "out of bounds" error. Try modifying your program to paint only when all of the source pixels are inside the image. Or, for a more difficult but more satisfying implementation, paint with only the portion of the brush that corresponds to pixels being read from within the image.
    7. Once your cloning tool is working, let's allow the user to adjust the brush size. To do so, declare an instance variable called brushSize of type int. In your mouseDragged method (where you do the copying), you are probably using a constant value of 10 as both the width and height of the region to be copied. Instead, use the value of the brushSize variable as the width and height, so the brush will be sized according to that variable.
    8. Add a mutator method for the brush size. This method should have the signature public void setBrushSize(int size) and it will pass into the method the percentage along the slider from left to right. Assign size/5 + 5 to the brushSize variable so that the brush size will vary from 5 to 25 pixels as you move the slider from left to right. After doing this, run your program again. YOPS should create a toolbar in which you can use a slider to adjust the brush size.
    9. You may notice a strange multiple duplication effect if the source and destination images of the cloning are the same image and the brush is not far from the source. This is to be expected because you are recopying areas that you copied from the original location.

    Implementation Hints:

    What To Turn In:

    Follow these submission instructions to turn in the following files and demonstrate your running program for a TA.

    1. cover-page.txt -- Be sure to fill in all information.
    2. Eyeball.java
    3. EyeballTool.java
    4. CloningTool.java (for optional extension 2)
    Reminder: All code you turn in should conform to the CSE131 Style Guide. Full credit will be given only if your work is well organized, clear, readable, and properly documented.

    Demo: After submitting your files, show a TA what your program does. You may be asked to make modifications to your program and/or predict what will happen if certain changes are made. Remember to complete your demo no later than your next lab section on or immediately following the due date.

    Kenneth J. Goldman (kjg@cs.wustl.edu)