The final design came to 15 classes. The program is driven by an "Animator" class. Animator is responsible for doing double-buffering, and managing animation in general. Since this class is the most intimately tied with the specifics of AWT, we did not design it in class: For the purposes of the lecture, Animator was given to us by another department :-) Animator periodically tells its Animatee to move forward in time, then it asks its drawables to draw themselves.
To handle overlapping, Animator simply queries each drawable for its height, and it draws the Drawables in order, from lowest to highest. To get acceptable performance, it doesn't re-draw any drawables whose height is <= 0. Because of this, Drawables that canMove() must know how to erase() themselves... In the case of Car, that's easy: Draw a car shape that's the color of the track. How animator is written is discussed in more detail in Animator's javadoc documentation.
Here's an OMT class diagram showing the overall design:
We only implemented one kind of track: A figure-eight track. Our design, however, leaves open the possibility of making any other kind of track layout. Here's a class diagram that illustrates how the track package works:
The design did change somewhat from the original scenario that was animated in class, but it's still close enough that it's instructive to look at that scenario. Here, then, is a high-level object interaction diagram that gives the basic flavor of what happens when a frame is requested: