Revision: 5775
Author: [email protected]
Date: Thu Jul 23 06:46:57 2009
Log: Created wiki page through web user interface.
http://code.google.com/p/google-web-toolkit/source/detail?r=5775
Added:
/wiki/WhyWidgetIsAClass.wiki
=======================================
--- /dev/null
+++ /wiki/WhyWidgetIsAClass.wiki Thu Jul 23 06:46:57 2009
@@ -0,0 +1,61 @@
+#summary Why Widget is an abstract base class
+
+One question that often comes up about the GWT Widget class is why it is
an abstract base class as opposed to an interface. After all, one should
always prefer an interface to a class wherever possible, right? And indeed,
there are many reasons to use interfaces, most notably that they're easier
to test and mock, and that you can create new extensions of them more
easily, because you can mix them into existing classes.
+
+So why is it that almost all client-side UI libraries, including GWT,
provide an abstract base class rather than an interface for
widgets/components? For example:
+ * GWT: `Widget`
+ * Swing: `JComponent`
+ * SWT: `Widget`
+ * Android: `View`
+
+One problem is that widgets tend to be "different", in that they have to
enforce a lot of constraints required by their underlying implementations.
SWT, for example, has to deal with the fact that win32 (and probably other
systems) requires that a parent window be known when creating a child
window. This is expressed in SWT through the constructor `Widget(Widget
parent, int style)`. Such a constraint cannot be expressed using an
interface.
+
+GWT's Widget class doesn't have to enforce that particular constraint, but
there are others. One example is the logic found in `setParent()`,
`onAttach()` and `onDetach()`, which contain very important, but subtle,
logic -- failure to implement this properly will result in extremely
difficult-to-debug memory leaks.
+
+== Workaround: Testing ==
+
+Admittedly, this introduces some difficulties. Let's say you're writing
unit tests for a custom widget, and you want to avoid using the Widget base
class, so that you can easily run tests without requiring a display. One
approach to this would be to create a View interface that is implemented by
both the concrete widget implementation and a mock implementation. But how
can you do this if you need it to be a Widget? An approach that has worked
well for a number of teams looks like this:
+
+{{{
+ interface View<T> {
+ Widget asWidget();
+ void setData(T data);
+ }
+
+ class MyView<T> extends Label implements View<T> {
+ public void setData(T data) {
+ setText(data.toString());
+ }
+
+ public Widget asWidget() {
+ return this;
+ }
+ }
+
+ class MockView<T> implements View<T> {
+ public Widget asWidget() {
+ return null;
+ }
+
+ public void setData(T data) {
+ // Whatever.
+ }
+ }
+}}}
+
+The `asWidget()` method allows you to avoid having `View<T>` extend
`Widget` at all -- only users of the view who need to actually add it to a
panel will even call `asWidget()`. And the actual call will be inlined away
by the compiler, which is nice.
+
+== Workaround: Union Types ==
+
+It is sometimes convenient to be able to write methods that require a
parameter that is both a widget, and also requires some interface to be
implemented by it. For example, say I want to write a method that simply
requires a `Widget` that implements `HasText`. There is no
`WidgetThatHasText` interface (one can imagine the interface explosion that
would stem from going down that path), and there couldn't be one because
`Widget` is a class. One solution to this problem is to use something like
the `View<T>` approach above. But you could also use Java's union types,
like so:
+
+{{{
+ setHasTextWidget(new Label());
+ setHasTextWidget(new HTML());
+
+ <W extends Widget & HasText> void setWidgetThatHasText(W foo) {
+ RootPanel.get().add(foo);
+ }
+}}}
+
+It's a bit verbose, but perfectly functional, and the user of the method
is not exposed to its verbosity.
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---