/* 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"); } }
©1994-2003 Kevin Boone, all rights reserved