FallingObject example |
In general, when we see classes that have common behaviors, we should consider creating a class to hold the common behavior, which we can then extend to the subclasses that define specific behavior. For example, we've seen several applications that involve moving objects, and in each there is some similar animation code.
For example, think about the leaves from the falling leaves example and the ball from our early "Pong" examples.
Demo: Falling Leaves Demo: Falling BallWhat do they have in common?
What is different about them?
Let's create a class, FallingObject, that captures the common features. When we create a FallingObject, we need to decide where it should fall to, what it should look like, and how fast it should fall. The constructor creates the FallingObject with these properties. The run method causes the object to fall at the specified speed and then hides it at the end.
Demo: Falling Leaves, etc. using theFallingObject
Now, let's look at the code. First, let's look at FallingObject. Both the leaves and the falling ball were originally ActiveObjects, so our new class, FallingObject will extend ActiveObject:
public class FallingObject extends ActiveObjectFallingObject has instance variables to hold:
Let's start by looking at the run method.
public void run() { while (object.getY() < fallToPos) { pause(DELAY_TIME); object.move(0, ySpeed); } object.removeFromCanvas(); }
We animate the object down the screen until it hits the final position, then we remove it from the canvas.
How do we modify FallingLeaf to make use of our new class? First, we have it extend FallingObject instead of ActiveObject:
public class FallingLeaf extends FallingObjectFallingLeaf has become a lot simpler. It no longer has any
instance variables or a run method. It inherits all of these from the FallingObject class.
The only thing that distinguishes a leaf from any other falling object is how it is created:
public FallingLeaf(DrawingCanvas canvas, Image leafPic, double x, double aSpeed, int aScreenHeight) { // first, call "up" to the constructor of the FallingObject class super(aScreenHeight, aSpeed); // create our leaf, but use the instance variable given in // the FallingObject instead object = new VisibleImage(leafPic, 0, 0, canvas); object.move(x, - object.getHeight()); start(); }
The constructor is responsible for creating a generic falling object and then customizing it, in this case to be a leaf. The generic falling object is created by the super call. super is a keyword that tells Java to call the constructor of the superclass, in this case FallingObject, passing it the height at which to disappear and its speed. The FallingObject constructor just saves these in instance variables:
public FallingObject(int thePos, double theSpeed) { ySpeed = theSpeed; fallToPos = thePos; // note that it is the responsibility of the derived class // to call start(); }
Next the FallingLeaf constructor creates the leaf-specific parts, then calls start to start the falling.
Given this, how can we create a falling ball? We can take advantage of our FallingObject class to get the instance variables and run method, and we just need to provide a constructor. Since we're putting this into the falling leaf example, we'll call our "falling ball" Hail
public class Hail extends FallingObject { // size of a hail pellet private static final int SLEET_SIZE = 10; // color for sleet private static final Color SLEET_COLOR = new Color(220,220,255); // initialize the instance variables and start the active object public Hail(DrawingCanvas canvas, double x, double aSpeed, int aScreenHeight) { // first, call "up" to the constructor of the FallingObject class super(aScreenHeight, aSpeed); // create our hail pellet object = new FilledOval(0,0,SLEET_SIZE,SLEET_SIZE,canvas); object.setColor(SLEET_COLOR); object.move(x, - object.getHeight()); start(); } }
These classes are both a lot simpler because we are reusing instance variables and methods from the FallingObject class.
Our Cloud doesn't create FallingObjects, as those have no visual representation, and in fact doing so would lead to errors, since no object is created unless we do so in our constructor in the class that extends FallingObject. So we create FallingLeaf or FallingSleet objects.
Let's think about what happens when we construct a Hail:
FallingObject example |