Aqua Phoenix
     >>  Lectures >>  Java 2  
 

Navigator
   
 
       
   

2.8 Homework Assignment 2 - Kaleidoscope

Due date and time: September 28, 2005 at 11:59:59pm (NOTE: Extension until September 29, 2005 at 11:59:59AM, that is: Noon)

Points: 16

This homework assignment is intended to make you more familiar with creating classes and using a basic inheritance model. You will use the results from this homework in a later assignment involving Java applets and graphics.

You will be building an inheritance model for geometric shapes, and randomly generate (instantiate) a number of them for testing purposes. The geometric shape model was introduced in class, and you are certainly allowed to use it as a framework. However, there are a number of methods and constructors that need to be added.

Place the code for the assignment in their respective object filenames, e.g. Shape in Shape.java. The manager class (runnable object), which tests your implementation and randomly generates shapes of various kinds should be named HW2.javaThe standard shapes you are to implement include: Rectangle, Square, Ellipse, Circle, and Triangle.

All of these objects share the following properties, which you should implement in the superclass named Shape:

  • Center position X: an integer
  • Center position Y: an integer
  • Fill color: a String (we will replace this later with a Color object)

Each shape has one or more properties that are unique, and must thus be implemented in the respective object classes:

  • Rectangle: width and height (integers)
  • Square: side (integer)
  • Ellipse: short and long radii, (integers)
  • Circle: radius (integer)
  • Triangle: side (integer) (this will be an equilateral triangle)

Every shape must have the ability to be created from 2 different constructors:

  1. takes all possible arguments for the given shape. A circle would be identified by its X,Y position, its Radius, and its Color. The rectangle's constructor would take the X,Y position, the Width and Height, and the Color.

    This constructor merely sets the object's instance variables equal to the passed values.

  2. takes all possible arguments except for the Color. A circle then takes the X,Y position, and the Radius.

    This constructor sets the object's instance variables to what has been passed, and generates a random Color.

The following methods must be implemented so that they apply to all shapes. If the method performs some task that is exactly the same for all shapes, then the method must only be implemented once. If the method performs a task that is specific to some shapes, then it must be implemented specifically for those shapes.

  • getX, which returns an integer for position x
  • getY, which returns an integer for position y
  • getColor, which returns a String for the color
  • setX, which sets the x position
  • setY, which sets the y position
  • setColor, which sets the Color
  • printInfo, which prints a line of text describing the object

The following methods must be implemented for specific shapes:

  • Rectangle: getWidth, getHeight, setWidth, setHeight
  • Square: getSide, setSide
  • Ellipse: getShortAxis, getLongAxis, setShortAxis, setLongAxis
  • Circle: getRadius, setRadius
  • Triangle: getSide, setSide

The hierarchy of inheritance is as follows:

  • Shape
    • Rectangle
      • Square (a special case of a rectangle)
    • Ellipse
      • Circle (a special case of an ellipse)
    • Triangle

Shape is a superclass to all other shapes, specifically Rectangle, Ellipse, and Triangle. Using the idea of inheritance, we can further structure Rectangle, and make it be the superclass to the shape Square. The same relationship holds for Ellipse-Circle. Thus, Square inherits all of its fields from Rectangle, and Rectangle inherits all of its fields from Shape. You must consider this structure when writing your classes.

To take full advantage of inheritance, you must consider the following:

  • Variables that are shared between a superclass and all of its subclasses should be defined in the superclass. It is important that these variables be accessible to the superclass and the subclasses, and should thus carry the modifier PROTECTED (as opposed to PRIVATE or PUBLIC). When defined in the superclass, the particular variable does not need to be defined again in any of the subclasses.
  • Methods in the superclass and subclasses that have the same purpose should be named the same. For example, the method printInfo() exists for all shapes, but prints slightly different information depending on the object (square vs. circle vs. ...) This method with this very name should appear in the superclass and all of its subclasses.
  • Remember not to implement the same code in every constructor, but to take advantage of the reference keyword this to call other constructors in the same class. (see the lecture notes for examples)
  • Remember to use the keyword super to refer to the superclass. This can be used to call constructors and methods in the superclass. Instead of re-implementing a common constructor, for example the one taking only x and y integers, use the common constructor in the superclass. For ellipse, for example, you would first call the superclass constructor to pass position values x and y, and afterwards set the remaining variables.

Notes:

  • When a subclass inherits from its superclass (i.e. extends that class), protected and public variables in the superclass can be used freely in the subclass, even though they were not defined in the subclass. If the superclass has a public or protected variable named X, then X can be used in the subclass without making special references to the superclass.
  • For Square and Circle, make sure to inherit from their respective superclasses. Because they Square is a special case of Rectangle and inherits from it, there should never be a need to deal with the superclass Shape. This is one of the great advantages of inheritance. When implemented correctly, a subclass only needs to know about its immediate superclass.
  • Since Square is a special case of Rectangle, width = height. Keep this in mind when writing the constructor for the Square class. A similar approach should be taken for Circle.
  • Random values are generated using the following static method call: Math.random() Math is a class and random() is a static method therein that returns a double type value between 0 and 1. In order to use this effectively for making a random selection among N items, multiply the value from Math.random() by N and cast the result as an int. The result is then an integer between 0 and N-1. To randomly select one among 5 items:

      int select = (int)(Math.random() * 5);
    You can then implement a series of if-else statements and perform some operation based on the value of select.
  • To select a random color, follow a similar scheme. Let's say you are interested in implementing 3 colors: red, green, blue. Generate a random number between 0 and 2, and implement a series of if-else statements to assign the strings of "red", "green", or "blue" to the color variable. I suggest implementing this part in the superclass Shape, so that you only need to write the code once. The Shape class should finally have 2 constructors:
    1. Takes position values x and y, as well as a String value for Color
    2. Takes only position values x and y, and then randomly generates the Color.

2.8.1 Driver class

The driver class (runnable class) should implement a loop and instantiate 20 shapes of random types. For each shape, you will choose random values for position and dimensions, and use the appropriate constructor to instantiate the object.

In your loop, you should randomly generate values for the position x,y. Since these values are common to all shapes, you can generate them in the loop, but outside of each if-else block.

You will implement a series of if-else statements to select the type of shape to create. In each if block, you will then:

  • Generate random values for the remaining values, e.g. rectangle: width and height, circle: radius, etc. The random color will be generated inside the objects.
  • Declare and instantiate the particular Shape object.
  • Call the appropriate method to print the object's information to the screen.

Here's an example output:

$ javac HW2.java
$ java HW2
  Circle: Position 5,8; Color red; Radius 9
  Square: Position 0,4; Color green; Side 2
  Rectangle: Position 9,2; Color red; Width 7; Height 1
  Circle: Position 2,3; Color magenta; Radius 3
  Ellipse: Position 9,3; Color yellow; Short Axis 6; Long Axis 9
  Trinanle: Position 1,1; Color cyan; Side 3
  ...
$

2.8.2 Recommended work-flow

Typically, one does not implement all details of a class, one class at a time. You should start with a framework, and then slowly add features. Here's a recommended work-flow:
  1. Implement Shape and position variables x and y
  2. Implement the Driver class to specifically instantiate a Shape object
  3. Implement Rectangle, and insure that it inherits from Shape. Update your Driver class to specifically test Rectangle.
  4. Implement Square, and insure that it inherits from Rectangle. Update your Driver class to specifically test Square.
  5. The relationship between Shape - Rectangle - Square is similar to Shape - Ellipse - Circle. Implement Ellipse and Circle next.
  6. Add the feature to produce a random color in the Shape superclass, and test it.
  7. Finally, modify Driver and include the loop and random generation of different Shape objects.

2.8.3 Submission instructions

Use the submit script to submit your assignment (instructions on class web site).