Kent said:
John C. Bollinger wrote:
Umm, do you really feel that hairsplitting on the
is-a/has-a boundary is a fruitful way to decide if a
design is robust? That's language lawyering, not
architecting.
I don't see any hair splitting here. On the contrary, I find it odd
that you assert that ButtonGroup.add(AbstractButton) should be expected
to have behavior governed by that of Collection.add(Object), simply on
the basis of /using/ Vector.add(Object) internally. If there is a
design error here then I would say that it is in ButtonGroup exposing
details of its implementation of add(), not in
ButtonGroup.add(AbstractButton)'s similarity or dissimilarity to
Collection.add(Object). You are the one who raised the issue of the
Collection contract, and thus I think it very relevant to observe that
ButtonGroup is not bound by that contract. Furthermore, Vector, which
*is* bound by that contract, fulfills it just fine without ever
returning false from its add(Object) method or throwing an exception
(though it might occasionally throw an Error).
That is _really_ frightening. Java, whose
"pointerless" design is nevertheless perennially
throwing null pointer errors, has designers who
think, and embed that thought in design, that their
code _cannot_ fail, for something as complex as
Vector? Brrrr.
Certainly one must either discard the idea of Java being pointerless or
allow that "NullPointerException" is misnamed, but I don't see how that
enters the picture. Classes throw that exception when presented with
nulls that they cannot handle, which is always the result of a bug,
usually one in the client class.
That Vector has no failure mode specific to it, however, is not so
strange. Vectors accept any and all elements, including null, so
Vector.add(Object) never fails on account of the argument. One must
presume that the method is not buggy, but even if it were, it is not
reasonable to expect a defined result if such a bug were triggered. The
remaining failure cases involve general VM conditions that are always
potential failures for any code; foremost among these is an
OutOfMemoryError. Such conditions are rarely, if ever, documented on
individual methods because they don't really "belong" specifically to
any method.
One certainly /could/ complain that Vector's design makes it difficult
to usefully extend the class. I would agree with that. I do not find
anything frightening about it, however. I also don't have much
complaint with not being able to extend Vector, as I have never seen a
good reason to want to do so (present discussion included). It is
almost always the case that giving a class a Vector member is more
appropriate than making it extend Vector.
I don't share your naive trust in the Java compiler
and runtime implementation.
I don't understand your concern. You ultimately have no alternative
*but* to trust the compiler, the runtime implementation, and the
platform library. One tests carefully, to be sure, and works around a
bug in any of those in the rare case that one is found, but *every*
behavior of your program is predicated on the compiler, VM, and library
behaving correctly. If you don't trust that then you cannot use the
language.
What on earth does that have to do with whether
adding to a ButtonGroup, which encapsulates an add
to a Vector, which _is_ a Collection, exposes the
exception that is supposed to be thrown to the
client when add() fails?
The internal addition to a Vector is an implementation detail, more
exposed than it ought to be but an implementation detail nonetheless.
Inasmuch as you are focusing on that detail, I think you are using a
faulty basis for your criticism. The implementation of a method or
class is driven by its design, not the other way around. Furthermore,
as I wrote before, even if you *do* look at the implementation, you have
to observe that the specific kind of Collection used (Vector) *doesn't*
have any defined failure mode for object addition. This is fully
consistent with Collection.add(Object), but more specific in that it
requires that every Vector be able to add every object. Given the
specifications of Vector.add(Object), ButtonGroup.add(AbstractButton)'s
use of that method does not provide any basis for an assertion that the
latter should provide some kind of avenue for a failure message to be
returned.
What on earth is "unintuitive" about an "at most
one" set of buttons?
It is not the "at most one" part so much as the details you described
about your implementation. If you use radio buttons in your button
group then the unintuitive part is that clicking on the selected button
turns it off. If you use check boxes then the unintuitive part is that
no more than one can be selected (though this is probably the most
intuitive of these unintuitive options). If you use some new and unique
(and visually distinguished) type of button then that in itself is the
unintuitive part.
In my case, I have a feature of a graphical display
which can have one of two states, or not appear, so
I want exactly "A or B or neither, but not both". I
_don't_ want to expend 50% more screen real estate
than is necessary to achieve that result.
The advantage of using the extra real estate is in providing an
interface that your users don't find surprising. Yes, your users will
likely learn whatever interface you provide, but it is less work for you
and them both if you provide controls and combinations of controls that
operate in a way that they will understand without special documentation
or support.
Button groups just don't usually work that way.
Because, according to its own documentation,
ButtonGroup _is_ the implementor of the JRadioButton
"exactly one" behavior; [...]
I don't mean ButtonGroup the class, I mean button groups as GUI
elements. They (should) provide "exactly one" selection from a group of
two or more. You do sometimes see button groups with no option
initially selected (more often on web forms than anywhere else), but the
meaning of such a group is nowhere near as clear as the meaning of a
similar group with an extra "none of the above" option selected. Coming
back to the ButtonGroup class, it provides Swing GUIs with button group
behavior consistent with button group behavior in other contexts. As
such, I don't find it lacking.
The "issue", if issue there is, is that the "at most
one" behavior is at least as common as the "exactly
one" behavior, but there is no "built-in"
ButtonGroup-paralleling mechanism to implement this
behavior.
As should be clear by now, I disagree that the behavior you are looking
for is as common as the behavior already provided by ButtonGroup. I
also disagree that the behavior you want is desirable, but if you want
to continue this discussion in that direction then I think it would be
best for both of us to go looking for relevant third-party usability
guidelines / studies / whatever (I have already provided a few such below).
The ButtonGroup Javadocs suggest instead using a
sep[A]rate, normal button that has the effect of
clearing the button group (by programmatically
selecting the invisible button), which to me seems
much more usable.
You apparently have screen real-state to give away
on a useless button; I don't. You have besides,
missed that the "none" button is recommended merely
as one of several possible ways to implement that
invisible button,
No, I didn't miss that, hence my use of the word "suggest." The
suggestion is the only specific one given in the ButtonGroup docs (v.
1.4; haven't checked v. 1.5). That in no way makes it a required
implementation, but it does distinguish that approach relative to other
implementations.
but by then you've already spent a
visible and distinguished button, making the
invisible button completely pointless, and thus that
recommendation purely bad advice.
You do have a point there. Indeed, I think the best option is a visible
"none of these" button in the button group. It is both clearest for the
user and easiest for the programmer. Are you developing for tiny
screens, or something? Otherwise you have an altogether different
problem if the required amount of extra screen space is a concern.
Selecting an already selected button, in the current
implementation, triggers the same signal as
selecting it when it was unselected, which is an
ambiguating design error by Sun.
Yes, I saw that when I looked into the question before my first
response. Here I'll agree that there is an error, though I would
identify it differently: the error is in using AbstractButton as a
superclass of both stateless buttons (JButton / JMenuItem) and stateful
ones (JCheckBox / JRadioButton). Or at least in defining some behaviors
there that shouldn't really be common to both types of button. There is
then a secondary error in allowing ButtonGroup to have any
AbstractButton as a member, when really it should be able to hold only
the latter type (already abstracted as JToggleButton).
Doing so should
either do nothing at all, or, as I'm seeking, clear
the button without setting another one. To avoid
that ambiguation of the two situations, the client
would have to do as I am trying to do (have done, in
the case, but without the joy of a better
implementation than Sun permits), by recording the
prior state so it can be checked whether the new
"selection" is merely a redundant click.
Yes, I would agree that a different type of button class (or different
behavior of the existing classes) would afford a better solution than
the type of shenanigans you have described implementing in your
ButtonGroup subclass. On the other hand, I wouldn't myself have
implemented any of it in the first place.
In your apparently limited experience, that might be
so. I've been designing GUIs roughly since that was
even possible, and there are no such "conventions"
known to me.
There are lots of GUI design manuals and guides, and for the most part
they describe very consistent behavior of the common GUI elements. You
cannot be unaware that these exist, particularly given your extensive
GUI design experience. Not all distinguish between "exactly one" button
group behavior and "at most one" behavior, however. Here are some:
http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html
http://developer.kde.org/documentation/standards/kde/style/basics/index.html
http://axp16.iie.org.mx/Monitor/v01n03/ar_ihc2.htm
http://benroe.com/files/gui.html
Notably, for HTML forms, RFC 1866 states "At all times, exactly one of
the radio buttons in a set is checked. If none of the <INPUT> elements
of a set of radio buttons specifies `CHECKED', then the user agent must
check the first radio button of the set initially." (section 8.1.2.4)
User agent compliance on this point varies, however.
The Gnome radio button docs state "Exactly one radio button should be
set in the group at all times. The only exception is when the group is
showing the properties of a multiple selection, when one or more of the
buttons may be in their mixed state."
(
http://developer.gnome.org/projects/gup/hig/draft_hig_new/controls-radio-buttons.html)
As to my GUI design experience (of which I do have some), it is not
nearly so important as my GUI *user* experience, because the latter is
where I get my expectations of how GUI elements should operate. I
daresay the same is true of most of your users. I have never
encountered a radio button that toggled off when I clicked it, and
therefore I do not expect such behavior. I have occasionally
encountered controls that looked like check boxes but acted like radio
buttons; this behavior is unexpected and thus less usable, but at least
it's easy enough to adapt to.
Sigh. Merely reading comprehension. I was still
writing about ButtonGroup, since the _behavior_ is
implemented at that level. Somehow, you lost grasp
of the discussion along the way when other terms
were mentioned. Better luck this time.
Do continue with the pot shots if your objective is to persuade me to
drop this conversation. Otherwise please refrain.
Your original post asked for opinions on whether ButtonGroup was
ill-designed for one or both of two reasons:
1) The lack of any success / failure information returned by
ButtonGroup.add(AbstractButton), and
2) ButtonGroup's lack of direct support for an "at most one" selection mode.
I take (1) as a general OO design question, and I responded accordingly.
I take (2) as an issue revolving around general GUI design and
behavior issues, and I responded accordingly, mostly with comments not
specific to Java or Swing. One cannot discuss whether or not
ButtonGroup's behavior is appropriate without some kind of context for
what its behavior ought to be. If you did not like the directions I
took the discussion then I can only apologize, but I assure you I did
and do have a firm grasp of it. For what it's worth, you have
significant control of the lines on which this discussion continues.