[ Suzanne Menzel | Sun Java Docs | DrJava | BackPack | Room | Adventurer | World ]


Java Workshop for High School Teachers
Stonehill College, May 16, 2004

The starter code used during the workshop can be found in the navigation bar above. Here's my final implementation:
[ BackPack | Room | Adventurer | World | SentientBeing | Ogre | ImageFrame | Util | shai.jpg | glass.au ]

Here's a pdf of the questions used during the CPS exercise. The percentages total to 33% because only 12 of the 40 remotes were actually used.

I've expanded the exposition of the topics below and included my diagrams, as appropriate.


Why Java?

The Java programming language was introduced by Sun Microsystems in 1995. It is an object-oriented, architecture-neutral, distributed, and multi-threaded programming language.

A Java program consists of one or more classes. For each class, the compiler generates portable bytecode in a .class file, which will run on any implementation of the Java Virtual Machine (JVM).

Java provides the following:


DrJava Development Environment

We'll start by downloading and installing the latest stable version of DrJava.


Text-Based Adventure Game

To describe and motivate the notion of a text-based adventure game, play StrongBad's game. Zork I, II and III are freely available at the Infocom home page. Here's information about how to play other Infocom games cheap, including my personal favorite: Hitchhiker's Guide to the Galaxy. For those of you who want to try out a text adventure game without having to bother downloading one, look no farther than emacs. If you type M-x dunnet (or ESC x dunnet), you'll start the game built into the program.


Welcome to My World

The first thing to do is to devise a map of the rooms in your world and their interconnections. We imagine that the exits in the room are labeled with some "direction" string. This could be something like "north" or "southeast" or something completely unexpected like "dennis".


Adventure Game Design

The adventurer will be modeled by an Adventurer class. When we create an Adventurer we will, at the same time, create his BackPack. Ask yourself which of the following statements makes more sense:

When we create an Adventurer, he must be placed in some Room. We have no need to create Adventurers whose location is unspecified.

Our goal is to implement the following hierarchy of classes:


The Collection Interface

Especially useful Collection methods are size, isEmpty, add, remove, contains, and iterator.

The Iterator methods are hasNext, next, and remove.

Classes in java.util that implement the Collection interface are ArrayList, Vector, Stack, HashSet. Because the contents of our BackPack and each Room is unordered and without duplicates, we choose to represent them with a HashSet.

Argueably the worst design decision of all time occured when some programmer opted to implement Stack as an extension of Vector. Why was this the wrong choice and what should have been done instead?

HashMap is a data structure that represents an unordered sequence of associations, i.e., key-value pairs.

> pairs = new HashMap();
> pairs instanceof Collection
false
> pairs.isEmpty()
true
> pairs.put("lucy", "ethel");
> pairs.put("pat", "vanna")
null
> pairs.put("lucy", "ricky")
"ethel"
> pairs.get("pat")
"vanna"
> pairs.get("vanna")
null

HashMap is an ideal data structure for representing the exit information associated with each Room.


Classroom Participation System

CPS is a "poll the audience" type system that allows students to respond anonymously to multiple-choice questions and then provides immediate feedback, giving the correct answer and the percentages of responses to each option. This system is manufactured by eInstruction and was featured in a recent NY Times article.


Adventure Game Classes


Multiple Inheritance

We may want to add non-player characters, such as an ogre, to our game. Since these characters probably share some traits with our adventurer, such as strength and location, we might decide to introduce a class named, say, SentientBeing that encapsulates that code which is common to all characters including the adventurer.

> shai = new Adventurer(dungeon);
> shai instanceof SentientBeing
true
> ogre = new Ogre(chasm);
> ogre instanceof SentientBeing
true
> ogre.getStrength()
20
> ogre.isDead()
false
> ogre.getLocation() == chasm
true
> ogre.go("north");
> ogre.getStrength()
18
> ogre.getLocation() == dungeon
true
> shai.give("wisdom", ogre)
"You don't have any wisdom."
> shai.give("evian", ogre)
"The ogre takes your offering, devowers it, and belches loudly."
> shai.drop("evian")
"You don't have any evian."
> shai.give("towel", shai)
"That was productive."

Now, consider what happens with Dennis. We might try to do something like the following:

A design like this is disallowed in Java because all subclasses must descend from exactly one superclass. This is called single inheritance. Have you ever wondered why Java does not support multiple inheritance?


The Diamond Problem
Frogosaur = Frog + Dinosaur;

The diamond problem is an ambiguity that arises when a class descends from two superclasses, both of which share a common ancestor. An illustrative example, due to Bill Venners, is inspired by Michael Crichton's novel Jurassic Park in which scientists combine dinosaur DNA with DNA from modern frogs to produce an animal that resembles a dinosaur but also exhibits certain frog characteristics.

As a precaution, only female animals are created with the intent of preventing the animals from reproducing. However, at the end of the novel, the scientists are alarmed to discover a nest of dinosaur eggs on the island.

It turns out that some frogs (and also quite a few fish) will undergo a gender reversal if their populace is dominated by one sex. Apparently, the Jurassic Park dinosaurs inherited this ability from their frog ancentry.

Here's the inheritance scenario:

Suppose that, when an Animal is created, it has a 50% chance of being male and a 50% chance of being female. The gender can be accessed with the getGender method. Frogs inherit these properties from Animal.

> a = new Animal();
> a.getGender()
'm'
> new Animal().getGender()
'm'
> new Animal().getGender()
'f'
> new Frog().getGender()
'm'
> new Frog().getGender()
'f'
> new Frog().getGender()
'f'

All Dinosaurs are created female.

> new Dinosaur().getGender()
'f'
> new Dinosaur().getGender()
'f'
> new Dinosaur().getGender()
'f'

It's possible for a Frog to switch gender after creation. Although every Frogosaur is female at birth, it inherits the gender switching capability from its Frog ancestory.

> kermit = new Frog();
> kermit.getGender()
'm'
> kermit.reverseGender();
> kermit.getGender()
'f'
> raptor = new Frogosaur();
> raptor.getGender() == 'f'
true
> raptor.reverseGender();
> raptor.getGender()
'm'

So where's the ambiguity? The problem occurs when a subclass of Animal overrides some method in the super class. Suppose, for example, that Animal defines getGender as follows, where gender is an instance variable:

public char getGender() {
  return gender;
}

Further suppose that Dinosaur overrides getGender with:

public char getGender() {
  return 'f';
}

Since Frog doesn't redefine getGender, it uses the version inherited from Animal. Now, what happens when a Frogosaur applies the getGender method? Does it use the version that it inherited from its Dinosaur parent and just return 'f' or does it use the version that it inherited from its Frog parent and return the value in the gender instance variable?

And what about gender? It is declared in Animal and, thus, inherited by Dinosaur and by Frog. Since Frogosaur inherits all data from both its parents, it will contain two copies of the gender variable.

We can achieve an approximation to multiple inheritance in Java by using interfaces.

Those properties of frogs that will be inherited by a Frogasaur will be made explicit by implementing a certain interface, such as GenderChangeable as shown here.

Unfortunately, we cannot abstract the common code for reverseGender because an interface contains the method headers, but no concrete implementation. The actual code for the methods is the responsibility of the implementing class.

Rather than using the predicate isReptile, we could have defined an interface named, say, Reptilian and set it up so the Frogosaur implements Reptilian, but AnemoneFish does not.

interface Reptilian {}

class Frogosaur extends Dinosaur implements GenderChangeable, Reptilian {
  :
}

class AnemoneFish extends Fish implements GenderChangeable {
  :
}

Here's how we'd find out whether or not a particular object has the Reptilian property.

> new Frogosaur() instanceof Reptilian
true
> new AnemoneFish() instanceof Reptilian
false


Swing

DrJava is particularly nice for exploring the javax.swing.* classes.


Teaching Strategies

It takes a great deal of effort to be a good teacher. Often it helps to do something unusual, atypical, or memorable. Props are always good.

For a discussion of the benefits of using atypical classroom techniques in a computer science class, read Mixed Nuts by Sid Stamm or visit (and contribute to) his Atypical Techniques online repository.


Recursion


Women in Computing

To Dream Tomorrow is a documentary by Jo Francis and John Füegi. This one-hour, scholarly, interdisciplinary film tells the story of Ada Byron Lovelace, her work with Charles Babbage, and their contributions to computing over a hundred years before the time usually thought to be the start of the Computer Age.

Daughter of a mathematically gifted, social activist mother and the "mad, bad and dangerous to know" poet, Lord Byron, Ada's life was unconventional, daring, and short. Possessed of enormous energy and talent, she faced some daunting obstacles - both in her personal life and the society of her time - as she fought to work professionally and make a contribution to science and mathematics.

Contact the film-makers for pricing information.


All materials copyright ©2003 Suzanne Menzel. Last updated May 18, 2004.