|
|
`Zener' example program: displays random Zener cards; demonstrates image handling
Zener.java
/*
Zener.java
This program displays a random Zener card. Zener cards are widely used in research
in parapsychology (e.g., extra-sensory perception). There are only five different
card types, called `waves', `cross', `circle', `square' and `star'. Many
experiments hinge on people attempting to guess (or determine by psychic means!)
which card will be drawn from the pack next.
Anyway, this applet simply displays a random Zener symbol each time it is
started. So the display will be any one of the following images:
  
(Note that you will only see the images if you are looking at the HTML version of this
file, Zener.java.html).
The Zener symbols are stored in GIF (image) files, so the `star'
image is in file `zenerStar.gif', and so on. When this applet executes, it
expects to find the image files in the same directory as the java `.class'
file. If not, it will not work.
The real purpose of this program is to demonstrate the use of inheritance. In this
application we have five different cards (wave, square, etc). Each is represented
as a separate class. All these classes have something in common: the ability to
display an image in a GIF file. The difference between classes is the exact name
of the file. So we create a class called `Card' which contains the functionality
needed by every card. Then we create classes called `Wave', `Square', etc.,
which implement what is different about each card. It turns out that the
classes that implement the individual classes are very simple; most of the
work is done in the Card class.
Note also that this program is very long for what it does; there are much simpler
ways to do the same job!
Kevin Boone, August 1999
*/
import java.applet.Applet;
import java.net.URL;
import java.awt.*;
/*
class Zener
This class is an Applet. Whenever it is re-drawn (that is, its `paint()' operation
called), it creates a new object of one of the Card subclasses, and calls its
`draw()' operation.
*/
public class Zener extends Applet
{
public void paint (Graphics g)
{
// Define a variable to store the `Card' object that will be created
Card card;
// To pick a Zener card at random, we will generate a random number
// between 0 and 4, and then use a switch statement to create a
// different Card object according to the number selected
int cardNumber = (int)(Math.random() * 5);
switch (cardNumber)
{
case 0: card = new Waves(); break;
case 1: card = new Cross(); break;
case 2: card = new Circle(); break;
case 3: card = new Square(); break;
default: card = new Star(); break;
}
// So at this point, `card' will refer to an object of class
// `Waves', `Cross', etc., picked at random
// The Applet class has built-in operations for loading image
// files into memory. The Card object we are defining does not. So we will
// set an attribute in the card object that specifies the
// current applet. The card will then call the operations in the
// applet that load the image. This is an example of `delegation', that is,
// using one object to assist the function of another.
card.ownerApplet = this;
// So now we've created the `card' object and told it the applet to use for
// loading images; not we call its `draw' operation to draw it on the
// screen.
/// QUESTION: why are we passing `g' as a parameter here?
card.draw(g);
}
}
/*
class Card
This class provides the functionality that is common to all the different cards
(square, wave, etc).
*/
///QUESTION: why is this class `abstract'?
abstract class Card
{
/*
The attribute `ownerApplet' will be set to indicate the applet that owns this
card object. This will be used to load the image file into memory before
drawing it on the screen. If we don't do this, we would need to provide
image loading facilities in the Card class, which would be a waste of time
*/
Applet ownerApplet;
/*
drawImageFile
This operation loads the image file specified by the parameter `imageFilename' and
draws it on the screen. We also pass a `Graphics' object to provide the
drawing facility.
*/
public void drawImageFile (Graphics g, String imageFilename)
{
// The getImage operation loads an image from a GIF file into an Image object.
// to do this it needs to know two things: the name of the iamge file, and
// the location (directory) at which it is stored. The operation
// `getCodeBase' in the Applet class will give the location of the applet.
// If we assume that the image files are in the same directory, then we
// can pass the output of getCodeBase directly to getImage. The
// image filename is supplied as a paramter to this operation
Image image = ownerApplet.getImage(ownerApplet.getCodeBase(), imageFilename);
// The drawImage operation draws the iamge on the screen. It needs to know the
// applet that will control the drawing process as well
g.drawImage(image, 0, 0, ownerApplet);
}
/*
draw
This operation draws the card on the screen.
*/
///QUESTION: why is this operation `abstract'?
abstract public void draw(Graphics g);
}
/*
Classes for indivdual card types.
Notes that these classes are very simple; they have only one operation (draw) which
calls drawImageFile with the appropriate filename
*/
class Waves extends Card
{
public void draw (Graphics g)
{
drawImageFile(g, "zenerWaves.gif");
}
}
class Cross extends Card
{
public void draw (Graphics g)
{
drawImageFile(g, "zenerCross.gif");
}
}
class Circle extends Card
{
public void draw (Graphics g)
{
drawImageFile(g, "zenerCircle.gif");
}
}
class Square extends Card
{
public void draw (Graphics g)
{
drawImageFile(g, "zenerSquare.gif");
}
}
class Star extends Card
{
public void draw (Graphics g)
{
drawImageFile(g, "zenerStar.gif");
}
}
|