| CIT 594 About the
Assignment 1 Sample Code CIT 594, Spring 2005 |
You should be able to figure out how to use and adapt my sample code without knowing exactly how it works. If you are interested, however, there are some things in it that we haven't discussed. This page is a brief guide to the code.
FloodFillApplet is an ordinary JApplet. The init() method
is perfectly typical--it creates the GUI, and installs an event handler, handleButtonClick().
There are only two interesting aspects of this applet: It uses a custom component
(ColorArrayComponent canvas) and it implements Observer.
The ColorArrayComponent is a component much like any other--a
button, say, or a text field. It displays a two-dimensional array of colors,
Color[][] colors, and it responds to mouse clicks on any cell of
the displayed array. It differs from other components in two respects. First,
I wrote it myself. Second, it uses Observer/Observable
rather than Event/Listener (more on this below).
The Sun-supplied Swing components extend JComponent, and ColorArrayComponent
is no exception. One nice thing about extending JComponent is that
when the component needs to be redrawn (for example, when the window is resized,
or covered and uncovered), Swing automatically calls my paintComponent(Graphics g)
method. (User code should never call paintComponent(Graphics g)
directly; instead, it calls repaint(), so that Java can do the
painting at an appropriate time.)
The constructor for ColorArrayComponent takes two arguments: the
Color[][] array to be displayed, and an Observer (The
FloodFillApplet supplies itself as the observer). It creates
a MouseHandler object (described below) and tells it about the
observer.
For painting itself when needed, ColorArrayComponent overrides
JComponent's default paintComponent(Graphics g)
method. This method gets the current width and height of the component
when called, so that the method works well when the applet is resized.
This is just a simple mouse listener. It is an inner class of ColorArrayComponent
so that it can easily find out the size of the array and the size of the component;
it needs this information to figure out the row number and column number on
which the mouse was clicked.
I would have preferred to extend MouseAdapter and override only
the mouseClicked(MouseEvent e) method. However, a Java class
can only extend one other class, and I needed to extend Observable.
It was only a little extra work to implement the MouseListener
interface and supply stubs for the methods I didn't care about.
I'm using the Observer design pattern, which is nicely supported
in Java by the Observer interface and Observable class.
The problem that this design pattern solves is that I wanted to decouple
the component from the code that uses it. That is, the component should be independent
of whatever GUI uses it.
One way to achieve this independence is to make the component an Observable.
An Observable takes a list of Observers (analogous
to listeners) and, whenever it wants to indicate that something happened, calls
setChanged() and notifyObservers(Object). These methods
tell Java to call the update(Observable, Object) method of
every registered Observer--the Observable doesn't
have to know anything about the Observers, or even if there are
any. The Object parameter is whatever information the Observable
wants to post about itself--in this case, a Point object containing
row and column indices.
To register itself as an observer, FloodFillApplet passes itself
(this) as an argument to the ColorArrayComponent,
which then calls MouseHandler's addObserver(observer)
method.
While the above more-or-less successfully decouples my new component from the GUI, it does have some problems. For one thing, by setting up the link in the constructor, I've limited the component to having only a single observer (that's just bad programming on my part). For another, Sun-supplied components use events and listeners, not observables and observers, so my code isn't as consistent with "the rest of the world" as it should be.
AWTEvent is just a normal class, and EventListener
is just a normal interface. What I should have done is this:
AWTEvent with a new class, say, ColorArrayEvent.
This new class would have getRow() and getColumn()
methods.MouseHandler class (which I would still need),
MouseEvent,MouseEvent to compute
row and column values,ColorArrayEvent,
andMouseEvent into a ColorArrayEvent.EventListener with a new interface, say, ColorArrayListener,
which requires some new method, say, mouseClicked(ColorArrayEvent).The advantage of this approach would be that users of a ColorArrayComponent
could just define a listener or listeners for it in the usual (and hopefully
familiar) way.
OK, why didn't I do things this way? Well, as usual, I was in a hurry, and I'm not exactly sure how to establish the connection between an event and the listener for that event. I believe I can figure it out fairly easily (some time when I'm not in a hurry), but by using Observer/Observable (which I know pretty well), I was able to get the code done before class.