A long time ago, on Mon, 2005-05-09 at 14:44 -0400, Owen Taylor wrote:
> I did some more fooling around today, and wanted to revisit an old
> discussion: [signal handling]
I've been saving this message for a long time, hoping I'd have a chance
to address it. So it's been almost 6 months. Oh well. First I'll write
my views on the subject, then inline reply to some of his comments.
Owen's basic concern seems to be the verbosity and clumsiness of the
Listener/Event pattern. When I first saw this pattern in java-gnome, I
freaked, something along the lines of "Oh my lord, what the hell is
this?" :)
But my GUI programming in Java predates Java 1.1. When I learned AWT, it
used a very basic event handling pattern. One of the reasons I never did
port that code from Java 1.0.2 (we're talking 1998 here) was that I was
completely mystified by the Listener/Event thing that the AWT in Java
1.1 introduced.
... time ... passes ...
As I understand it from Mark Howard and Jeff Morgan, they added the
Listener/Event pattern for signal handling to Java because they felt it
was the more familiar idiom for experienced Java programmer, especially
those coming from Swing or AWT.
I believe that this was absolutely the correct decision to make. In
fact, as I've looked around a bit (and over enterprise code that I've
run (though didn't write - I'm an operations guy) I've realized just how
prevalent this pattern is in the Java world. It's everywhere - EJB, JMS,
all over the J2EE stack.
And so, even though it's somewhat (ok, very) cumbersome **especially
from the viewpoint of someone coming from the GTK C world**, it is
natural indeed for someone coming from the Java world, and that
(unscientifically) is the bulk of our userbase.
++
Ok. So I've said that it's ok. But that doesn't mean it can't be
improved - or ditched. To be honest, I'd like to see a better model.
But switching will be tricky indeed. We already have 2 1/2 APIs for
signal handling. [the Listener/Event pattern which most of us use, then
the raw Event handling which happens to be exposed, and also method name
hookups if you care to use Glade for that]
When I was in Toronto last week I met with some of the guys (and Hiro,
who came up from Waterloo!) and discussed this issue among others. The
biggest problem I see is that we already are close to the boundary of
having unmaintained/unsupported APIs, and if we don't do a clean break
(not just deprecating but hard and fast REMOVING the old APIs in favour
of the new one) then we will end up with an even worse situation.
The only possible place for us to make such a break is at a major
version number jump (ie libgtk-java moves from 2.x.y to 3.w.z) ... which
is hard to do unilaterally as we are now somewhat historically tied to
following the underlying GTK release numbers. [Note to self - moving
from java-gnome 0.8.3 to java-gnome 2.4.x was probably a bad idea - we
lost the freedom to make a major API change on our own schedule].
To be honest, I don't much care about this - in the modern software
world, 1.2 to 1.4 IS a major release. So if we need to change the signal
handling we can probably do so whenever, but we should do so with lots
of warning and RIP OUT whatever old models we're no longer going to
support.
++
Now on to specifics. If we do change (and incur all this administrative
burden I've just been talking about, not to mention forcing a port of
any and all applications), then we should make it a good one. There are
all sorts of areas of the API that could do with improvement & redesign.
In the case of signal handling, there seem to be two broad option paths,
revolving around typing.
The present system is strongly typed. We have KeyListeners and KeyEvents
and TreeViewListeners and TreeViewEvents and also interfaces with
similar APIs, eg TreeModelFilterVisibleMethod. The advantage of this
is that we allow ourselves to take advantage of the strengths of the
Java language, getting appropriate methods for appropriate events, and
having being able at compile time to catch wrong API mistakes.
The other branch of options involve not using the type safety system at
all, and doing all the lookups by strings. This is more "traditional"
GTK, but also is less ideal programming practice because it means that
you have to wait until runtime to find out if you misspelled a signal
name. The usual result of such a bug is that simply nothing happens, but
likewise situations where the wrong method gets called arise. Worst of
all you loose any support that IDEs like Eclipse can give you both at
code writing time and also at debugging time (call stacks become a
mess). The upside is that the API is really simple, although not quite
as simple as in C or Perl as one has to fight through a class / object
somewhere to get to the method name you're trying to call.
For me the promise of absolute simplicity is not worth the cost of not
leveraging the strongly typed character of the language we're working
in. But in the end, I'd make my vote on the basis of how elegant our
proposed new system would be in use.
++
To close, some replies to Owen's comments: his first suggestion
> > button.connectClicked (new ButtonClickedListener () {
> > public void run(Button button) {
> > System.out.println("Button was clicked");
> > }
> > }
Isn't too bad. Strongly typed.
> > Creating one helper class for each signal might be a bit
> > expensive...
Expensive in authoring and a little less so in maintenance to be sure.
But in runtime terms Java is already stacked full of objects. 20-300 for
signals isn't going to hurt anything.
Owen's second message notes that this is nicer:
> button.connect(new Button.Clicked() {
> public void clicked(Button button) {
> System.out.println("Button was clicked");
> }
> });
and I agree, although in general I would note that even something as
simple as Button has a plethora of signals that need implementing: from
Button.Type I see ACTIVATE, CLICK, ENTER, LEAVE, PRESS, and RELEASE. For
all I know there are more down in GTK that we haven't yet properly
wrapped.
> (Maybe use
> 'onClicked' rather than 'clicked' as the method name?)
[is click or clicked the underlying event-signal name?]
Not sure. Java's APIs (and java-gnome's) are schitzo in this regard.
You've got stuff like next() and item() and present() and activate()
floating around, but then set*, get*, and is* prevails for all the
property accessors and mutators. Perhaps on* as a family name isn't a
bad idea.
I have some thoughts on the API design sweepstakes, but this email is
already too long as it is. I'll follow up if there is indeed interest in
radically changing things. Otherwise, if we're just going to leave
things be, then let's just keep things well maintained and work on
fixing, if nothing else, the JavaDoc for all the bloody nested Type
statics. I remember how hard it was to learn our event handling API,
even with some examples to follow.
AfC
Bangalore
|