The Nervous Shapes Example This Example is taken from Dr. King s Java book 1
11.6 Abstract Classes Some classes are purely artificial, created solely so that subclasses can take advantage of inheritance. The Vehicle class was created for convenience in the real world, there are no generic vehicles, only specific types of vehicles. The same reasoning applies to the Account class: banks offer only specific kinds of accounts. In Java, artificial classes like Vehicle and Account are called abstract classes. 2
Characteristics of Abstract Classes The declaration of an abstract class must include the word abstract, which is usually placed just before the word class. Some of the methods in an abstract class may be abstract methods. An abstract method is a dummy method that has no body: public abstract double doublevalue(); It is illegal to create an instance of an abstract class. 3
Uses of Abstract Classes Abstract classes are useful only as starting points for defining subclasses. They often arise from bottom-up design, where the strategy is to first identify which classes are needed and then look for ways to factor out whatever those classes have in common. The result is an abstract class that can be extended to create the classes that are actually needed. 4
Example: A Shape Class Suppose that the need arises for a series of classes that represent specific geometric shapes, such as Circle and Rectangle. These classes have much in common, so work can be saved by first creating a generic Shape class. No Shape objects will ever be created. Instead, the Shape class will serve solely as a starting point for defining more-specific shape classes. 5
Example: A Shape Class Shape will have instance variables representing the properties that are common to all shapes: Location (a pair of x and y coordinates) Color (a Color object) Every shape will also have a width and a height. It might not be a good idea for the Shape class to have width and height variables, which would force all subclasses to inherit these variables. Some subclasses, such as Circle, won t need these variables. 6
Example: A Shape Class Shape will have instance methods representing the behaviors that are common to all shapes: draw move getx, gety, getcolor, getwidth, getheight setcolor The draw, getheight, and getwidth methods will have to be abstract they can t be written without knowing what kind of shape is involved. 7
Shape.java // Represents a geometric shape that can be displayed in a // graphics context import java.awt.*; public abstract class Shape { // Instance variables private int x; private int y; private Color color; // Constructor protected Shape(int x, int y, Color color) { this.x = x; this.y = y; this.color = color; 8
// Abstract methods public abstract void draw(graphics g); public abstract int getheight(); public abstract int getwidth(); // Other instance methods public Color getcolor() { return color; public int getx() { return x; public int gety() { return y; 9
public void move(int dx, int dy) { x += dx; y += dy; public void setcolor(color color) { this.color = color; 10
The Circle Class The Circle class will need a diameter instance variable. Circle will need to override the abstract methods that were inherited from the Shape class: draw, getheight, and getwidth. 11
Circle.java // Represents a circle that can be displayed in a graphics // context import java.awt.*; public class Circle extends Shape { // Instance variables private int diameter; // Constructor public Circle(int x, int y, Color color, int diameter) { super(x, y, color); this.diameter = diameter; 12
// Instance methods public void draw(graphics g) { g.setcolor(getcolor()); g.filloval(getx(), gety(), diameter, diameter); public int getheight() { return diameter; public int getwidth() { return diameter; 13
The Rectangle Class The Rectangle class will need width and height instance variables. Like the Circle class, Rectangle will need to override the draw, getheight, and getwidth methods. 14
Rectangle.java // Represents a rectangle that can be displayed in a graphics // context import java.awt.*; public class Rectangle extends Shape { // Instance variables private int width; private int height; // Constructor public Rectangle(int x, int y, Color color, int width, int height) { super(x, y, color); this.width = width; this.height = height; 15
// Instance methods public void draw(graphics g) { g.setcolor(getcolor()); g.fillrect(getx(), gety(), width, height); public int getheight() { return height; public int getwidth() { return width; 16
11.9 Case Study: Nervous Shapes The NervousShapes program will create a frame containing a random mixture of circles and rectangles with random colors, sizes, and positions. After a brief delay, the shapes will be moved to slightly different positions, with the direction of motion for each shape chosen randomly. 17
Program Behavior The new x coordinate for each shape will either be the same as the old x coordinate, one pixel smaller, or one pixel larger. The new y coordinate will be computed in a similar manner. The size of the frame s drawing area will be fixed at 200 pixels by 200 pixels. The number of shapes displayed will be 50. The delay time the interval between animation cycles will be 10 milliseconds. 18
Program Behavior 19
Design of the NervousShapes Program The program will consist of three steps: 1. Create a frame labeled Nervous Shapes. 2. Create 50 random Circle and Rectangle objects. 3. Display the shapes within an animation loop. Each step will be done by a helper method. The helper methods will be named createwindow, createshapes, and animateshapes. createwindow is easy to write. 20
The createshapes Method createshapes will need a shapes array in which to store the 50 shapes that it creates. Because animateshapes will also need access to the shapes array, it will be a class variable. shapes will be declared as an array of Shape objects so that it can store both Circle objects and Rectangle objects. 21
The createshapes Method createshapes will execute the following steps 50 times: 1. Select a color in which each component is a random number between 0 and 255. 2. Decide whether the new shape will be a circle or a rectangle. 3. If it s a circle, choose a random diameter between 10 and 20, choose a random position, create a Circle object, and store it in the shapes array. If it s a rectangle, choose a random width and height between 10 and 20, choose a random position, create a Rectangle object, and store it in the shapes array. 22
The animateshapes Method animateshapes will consist of an infinite loop that repeats the following steps: 1. Clear the frame s drawing area. 2. Move each shape in the shapes array to a new position and display it. The new x coordinate for each shape will be the same as the old one, one pixel smaller, or one pixel larger; the new y coordinate is computed in a similar fashion. A move is not performed if it would cause any part of the shape to go outside the frame s drawing area. 3. Call repaint to update the screen. 4. Pause briefly. 23
The animateshapes Method To display element i of the shapes array, animateshapes will call the draw method: shapes[i].draw(g); Thanks to dynamic binding, there s no need to test whether shapes[i] is a Circle object or a Rectangle object before calling draw. 24
The generaterandomint Method One more helper is needed: generaterandomint, which returns an integer chosen randomly from a specified range of integers. 25
NervousShapes.java // Program name: NervousShapes // Author: K. N. King // Written: 1999-08-12 // // Displays a frame containing a random mixture of circles // and rectangles with random colors, sizes, and positions. // The shapes periodically change position, with the // direction of motion chosen randomly for each shape. The // new x coordinate for each shape will either be the same // as the old x coordinate, one pixel smaller, or one pixel // larger; the new y coordinate will be computed in a // similar manner. Shapes will be constrained so that they // do not move outside the drawing area. import java.awt.*; import jpb.*; 26
public class NervousShapes { // Constants private static final int DELAY = 10; // Animation delay (milliseconds) private static final int MAX_SIZE = 20; // Maximum width and height of a shape private static final int MIN_SIZE = 10; // Minimum width and height of a shape private static final int NUM_SHAPES = 50; // Number of shapes private static final int WINDOW_SIZE = 200; // Width and height of drawable portion of frame // Class variables private static DrawableFrame df; // Frame in which shapes are displayed private static Graphics g; // Graphics context for frame private static Shape shapes[] = new Shape[NUM_SHAPES]; // Array of shapes 27
public static void main(string[] args) { createwindow(); createshapes(); animateshapes(); 28
/////////////////////////////////////////////////////////// // NAME: createwindow // BEHAVIOR: Creates a frame labeled "Nervous Shapes", // displays the frame, and sets the size of // the frame (using the WINDOW_SIZE class // variable). Assigns the frame to the df // class variable, and assigns the frame's // graphics context to the g class variable. // PARAMETERS: None // RETURNS: Nothing /////////////////////////////////////////////////////////// private static void createwindow() { // Create a frame labeled "Nervous Shapes" and set its // size df = new DrawableFrame("Nervous Shapes"); df.show(); df.setsize(window_size, WINDOW_SIZE); // Get the frame's graphics context g = df.getgraphicscontext(); 29
/////////////////////////////////////////////////////////// // NAME: createshapes // BEHAVIOR: Creates enough Circle and Rectangle objects // to fill the shapes array. Each shape has a // random color, size, and position. The height // and width of each shape must lie between // MIN_SIZE and MAX_SIZE (inclusive). The // position is chosen so that the shape is // completely within the drawing area. // PARAMETERS: None // RETURNS: Nothing /////////////////////////////////////////////////////////// private static void createshapes() { for (int i = 0; i < shapes.length; i++) { // Select a random color int red = generaterandomint(0, 255); int green = generaterandomint(0, 255); int blue = generaterandomint(0, 255); Color color = new Color(red, green, blue); 30
// Decide whether to create a circle or a rectangle if (Math.random() < 0.5) { // Generate a circle with a random size and position int diameter = generaterandomint(min_size, MAX_SIZE); int x = generaterandomint(0, WINDOW_SIZE - diameter); int y = generaterandomint(0, WINDOW_SIZE - diameter); shapes[i] = new Circle(x, y, color, diameter); else { // Generate a rectangle with a random size and // position int width = generaterandomint(min_size, MAX_SIZE); int height = generaterandomint(min_size, MAX_SIZE); int x = generaterandomint(0, WINDOW_SIZE - width); int y = generaterandomint(0, WINDOW_SIZE - height); shapes[i] = new Rectangle(x, y, color, width, height); 31
/////////////////////////////////////////////////////////// // NAME: animateshapes // BEHAVIOR: Establishes an infinite loop in which the // shapes are animated. During each loop // iteration, the drawing area is cleared and // the shapes are then drawn at new positions. // The new x and y coordinates for each shape // will either be the same as the old ones, // one pixel smaller, or one pixel larger. A // shape is not moved if doing so would cause // any portion of the shape to go outside the // drawing area. At the end of each animation // cycle, there is a brief pause, which is // controlled by the delay constant. // PARAMETERS: None // RETURNS: Nothing /////////////////////////////////////////////////////////// private static void animateshapes() { while (true) { // Clear drawing area g.setcolor(color.white); g.fillrect(0, 0, WINDOW_SIZE - 1, WINDOW_SIZE - 1); 32
for (int i = 0; i < shapes.length; i++) { // Change the x coordinate for shape i int dx = generaterandomint(-1, +1); int newx = shapes[i].getx() + dx; if (newx >= 0 && newx + shapes[i].getwidth() < WINDOW_SIZE) shapes[i].move(dx, 0); // Change the y coordinate for shape i int dy = generaterandomint(-1, +1); int newy = shapes[i].gety() + dy; if (newy >= 0 && newy + shapes[i].getheight() < WINDOW_SIZE) shapes[i].move(0, dy); // Draw shape i at its new position shapes[i].draw(g); // Call repaint to update the screen df.repaint(); 33
// Pause briefly try { Thread.sleep(DELAY); catch (InterruptedException e) { /////////////////////////////////////////////////////////// // NAME: generaterandomint // BEHAVIOR: Generates a random integer within a // specified range. // PARAMETERS: min - the lower bound of the range // max - the upper bound of the range // RETURNS: A random integer that is greater than or // equal to min and less than or equal to max /////////////////////////////////////////////////////////// private static int generaterandomint(int min, int max) { return (int) ((max - min + 1) * Math.random()) + min; 34
Demo Source code is in your D2L account 35