Sunday, November 4, 2012

JavaFX and the Missing Interfaces

During the development of my company's JavaFX based application framework, I have often been challenged by the extensive use of superclasses rather than interfaces in JavaFX. Here are my thoughts on what could be done to improve JavaFX in this regard.

The Single Inheritance Problem

Being a feature rich API, it is no surprise that JavaFX is quite complex, containing more than 700 class files. More surprisingly, however, is the fact that the API contains very few interfaces – even key extension points like Node, Parent, Shape, and Control are all implemented as classes. This gives the JavaFX API developers more control over what is going on at runtime, since final methods can be placed in these classes, thereby enforcing specific runtime behavior. From a design perspective, however, it does not work well in Java's single inheritance world.
Problems arise when application developers and third party framework developers extend JavaFX classes to add more features. As an example, an essential feature of my company's framework is internationalization, and to enable it across all of the user interface, I have extended each of the built in JavaFX controls; my internationalizable text field is called ITextField, and it extends TextField. Similarly, IComboBox extends ComboBox, IButton extends Button – you get the picture.
Since all of these classes have a number of common methods, they all implement a common interface called IControl. When an application developer sees a class implementing IControl, he will immediately be aware that a number of additional features are available.
Some parts of my framework add cross cutting features, and some of those features work only for IControls; consequently I have API methods similar to
public Something fooBar(IControl control) {…}
Although this guarantees that the control parameter is internationalizable, there's a catch. Have you spotted it? Since the parameter is required to be an IControl there is no longer a compile time requirement that it is also a Control. Why? Well, since Control is a class – not an interface – there is no way I can make my IControl interface extend Control, to signal that only Controls should be IControls. So, if a developer has not yet realized this requirement, he could turn any class into an IControl by implementing the interface – it does not even have to be a scene node. Obviously, my framework will fail at runtime if the developer implements IControl from a non-control class; but there is no way to reveal the mistake at compile time.
As an additional nuisance, this problem also requires me to do a lot of casting internally in my framework code. Everytime I implement a method that takes an IControl parameter, I have to cast it to Control to invoke Control spefic methods like setTooltip() or Node specific methods like isVisible() or getBoundsInLocal().

A Solution

From the many constructive discussions I have had with Richard Bair, Jonathan Giles and other JavaFX API developers, it is crystal clear to me, that backwards compatibility is valued immensely – and with good reason. The upside is that new versions of JavaFX does not break any code – yours, or mine, or the UI of a critical application used at a hospital or a nuclear power plant. That is a good thing. However, the downside is, it is now very hard to fix the missing interfaces problem.
In an ideal world, I would like a dozen JavaFX classes to be rewritten as interfaces. The existing Control class would be renamed to ControlBase, allowing it to implement an interface called Control. Obviously this would immediately break the code of each and every JavaFX application on the planet, so that idea does not go on my Christmas wish list this year. However, we are not prevented from introducing an interface of a different name. So while we cannot get
NodeBase implements Node 
ParentBase implements Parent
ShapeBase implements Shape
ControlBase implements Control
we can get
Node implements SceneNode 
Parent implements SceneNodeParent 
Shape implements SceneShape
Control implements SceneControl
where each of the SceneX interfaces simply contain all of the public methods of their class counterpart.

Conclusion

Admittedly, this does not follow any de facto Java naming standard, but it does work – without breaking any existing code, these interfaces solve the design problem. I can now rewrite my IControl interface as
interface IControl extends Control {…}
thereby requiring all IControl's to be real Controls. If I have very finicky requirements for an input parameter, I can even specify new interfaces which combine requirements, like
interface Demanding extends Control, LooksLikeADream, SmellsLikeRoses
{…}
which means that a method requiring a Demanding parameter will get exactly what it needs.
I warmly welcome any constructive feedback on this suggestion, either here or on the openjfx-dev mailing list, before I post a feature request to the JavaFX team.

7 comments:

  1. Very interesting, I noticed that there are very few interfaces in JavaFX but I don't think it is a problem. It would be almost impossible to re-implement Node with the correct behaviour without it having bugs.

    Extending controls also has some issues. For example, if you create an InternationalizableTextField and I create an AutoCompleteTextField, and then want to share behaviours we are at an impasse. Most requirements can be met by using listeners, although it certainly wouldn't be fun adding a listener to every TextField.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. You can do something like: https://gist.github.com/4065802

    It is not very convenient, but I think does what you want?

    ReplyDelete
  4. You are right, Ivan. But the problem with that approach is that it forces me to scatter the
    <T extends Control & IControl>
    all over the API. It works, but as you say... not pretty.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Interfaces for controls would defiantly be a nice feature. I especially would like to supply my own implementation of ListView.

    But as for the core classes like Node I think that every alternative implementation would have to be tied to the non-public APIs of the rendering engine. Correct me if I'm wrong but for the Node to be totally decoupled I think you would really have to implement a more complex interface with methods for the rendering engine.

    On the other hand I'm wondering if anyone is considering to take JavaFX rendering to the Web in the long future. Like for example Eclipse RAP does with SWT or maybe with an approach like GWT based on j2js compilation. Interfaces instead of classes could help with this.

    ReplyDelete
  7. Woods include usually performed a crucial portion inside peoples' lives, by offering color along with maintaining the sweetness of the spot for a fruit along with plants which attract fauna! Whenever you are around timber you feel an expression involving contentment along with calm and even more essentially, safety.Træfældning

    ReplyDelete