Proposal: Deprecate Builders

Richard Bair richard.bair at oracle.com
Mon Mar 25 13:31:58 PDT 2013


Ya it was a good read (thanks Ali). I think Michael does a good job recounting the problems with this approach. I wouldn't use generics for it (I don't trust them for this kind of thing -- stay in the mainstream usage and generics are great, start getting clever and we've been burned over and over. I don't believe anybody when they tell me it will work any more :-)). Instead override with covariant return types.

I think that is possible, but it would be painful to add all the withers to all the properties for the entire public API. But I do think that once we've got the builders deprecated, we can then take a long look at what to do instead -- using lambdas for example. There might be other ways to get what we want that are much less intrusive. Or maybe just have an annotation processor that will auto-generate the builders of your choice and let it be a 3rd party thing.

Richard

On Mar 25, 2013, at 1:09 PM, Tom Eugelink <tbee at tbee.org> wrote:

> 
> Basically that is very close to my "withers" suggestion, only I chose not to beak Java Bean API specs and keep the setters void.
> 
> Tom
> 
> 
> On 2013-03-25 21:01, Ali Ebrahimi wrote:
>> Hi Richard,
>> Today is the day I predicted two years ago and proposed alternative for
>> Builders.
>> this is : https://javafx-jira.kenai.com/browse/RT-13851
>> 
>> Best regards,
>> Ali Ebrahimi
>> 
>> On Mon, Mar 25, 2013 at 10:05 PM, Richard Bair <richard.bair at oracle.com>wrote:
>> 
>>> We made a mistake. When we released JavaFX 2.0 we included a (large) set
>>> of *Builder classes to provide a builder-pattern approach to building
>>> JavaFX UIs. The builder-pattern approach provides several very nice
>>> features:
>>>         - Ability to setup generic configuration once and "stamp out"
>>> multiple copies
>>>         - Structured code style that closely approximates the "container
>>> hierarchy" of the UI
>>>         - Strongly-typed "declarative" style programming
>>> 
>>> The Builders did come at a cost. There are a lot of them, and if used,
>>> require more class loading which slows down startup. When Pack200 is not
>>> being used, they represent a sizable fraction of the code base. They
>>> clutter up the JavaDoc (although this could be solved by having a "Builder"
>>> section like there is an "Enum" section and "Interface" section).
>>> 
>>> But all those advantages and costs were weighed and we concluded Builders
>>> were worth it, and they went in. Based on the above, I would still conclude
>>> that Builders are OK. However, it turns out our implementation has some
>>> intractable problems with respect to binary compatibility.
>>> 
>>> In order to ensure our Builders are always up-to-date with the latest API
>>> added to the core classes, the Builders are auto-generated using an
>>> annotation processor. Also, in order to keep the size of the builders small
>>> relative to the rest of the platform, we employ complex use of generics in
>>> order to allow the builders to inherit. That is, ControlBuilder extends
>>> from ParentBuilder extends from NodeBuilder just as Control extends from
>>> Parent extends from Node. But accomplishing this was no easy feat. Although
>>> the "id(String)" method is defined on NodeBuilder, it must return a
>>> ControlBuilder when used from the ControlBuilder. All of this was
>>> accomplished using generics, and as it turns out, depended on two bugs in
>>> JDK 6 and JDK 7 in order to work (although we didn't know it at the time).
>>> Those bugs were fixed in JDK 8, and as a result, the builders no longer
>>> work correctly for certain cases. Just about any option we take is going to
>>> lead to a binary incompatibility in 8, however we find this unacceptable.
>>> If we cannot avoid it, then whatever we choice we make for 8 had better be
>>> a final solution -- not something we will later decide we need to tweak
>>> further.
>>> 
>>> And this is what puts us in a tight spot. After having painfully looked
>>> over all of the alternatives, it seems to come down to this:
>>>         a) Flatten the builder hierarchy so that ControlBuilder extends
>>> Object the same as ParentBuilder would and all other Builders.
>>>         b) Stop using builders. Phase them out in a controlled manner.
>>> 
>>> Option (a) works because we remove all generics from the equation. There
>>> are some 73 or so methods on NodeBuilder. These would be redefined on the
>>> Builder for each class that extends from Node. This solution works by
>>> solving the root problem -- generic use in builder subclassing scenarios.
>>> This solution however changes the cost/benefit analysis for Builders,
>>> because now they would then comprise a much larger fraction of the overall
>>> platform size. And this runs at cross purposes with embedded use cases.
>>> 
>>> Option (b) works by removing Builders from the scenario entirely. Because
>>> this would be a *huge* breaking change, it would have to be managed
>>> carefully in a way that, in fact, won't break anybody (for at least several
>>> years). To implement this option, we would:
>>>         1) Add @Deprecate to all Builders with a URL in the description to
>>> a page describing the change and how to migrate
>>>         2) Stop auto-generating builders. We'd take whatever we had in the
>>> latest 2.x line and reuse them as-is.
>>>                 - This means that as new API is added to the platform, the
>>> builders wouldn't be updated to match
>>>         3) Remove the Builders from the Java 8 JavaDoc
>>>         4) Split the builders into a separate jar from jfxrt.jar. It would
>>> also be on the java.ext.libs path so that it is automatically picked up so
>>> that people are not broken when running FX 2.x apps on 8.x without
>>> modification. They'd still work.
>>>         5) Provide a download link to the fx2-builders.jar containing all
>>> these builders.
>>>         6) Encourage people to use fx2-builders.jar instead of relying on
>>> builders being on the class path by default
>>>         7) In Java 9, remove the builders from the class path (this is
>>> several years down the road)
>>> 
>>> This proposal is essentially saying that the fix to make Builders work
>>> correctly reduces the value of the builders to the point where they just
>>> aren't worth it, and when considering Mobile / Embedded use cases, the
>>> extra cost of the builders is prohibitive. It also is based on the theory
>>> that the Builders are not being used by everybody, and so many people would
>>> not be negatively impacted but rather only positively impacted by no longer
>>> paying for the additional cost in bytes for the builders. It also
>>> recognizes that people who *are* using the builders and *do* like the
>>> programming style (including Jasper and many of our own) will see a loss of
>>> functionality that they have grown to like.
>>> 
>>> The other thing to bear in mind is that most of the benefits of Builders
>>> don't have to come from Builders -- there are alternatives. For example,
>>> the 'Ability to setup generic configuration once and "stamp out" multiple
>>> copies' benefit can also be achieved by helper methods.  The 'Structured
>>> code style that closely approximates the "container hierarchy" of the UI'
>>> advantage can be accomplished using FXML instead, or by using this pattern
>>> (which creates a boat-load of inner classes so it has its own problems):
>>> 
>>> // The first set of braces defines it as an anonymous subclass, the second
>>> set as an initializer
>>> Group g = new Group() {{
>>>     getChildren().add(new Button() {{
>>>         setText("Hello");
>>>     }});
>>> }};
>>> 
>>> Another option which we've discussed a bit is whether we could use
>>> lambda's to do configuration in a builder-like style in Java 8, somewhat
>>> like Groovy does. So for example:
>>> 
>>> Group g = new Group(gg -> {
>>>     gg.getChildren().addAll(new Button(b -> {
>>>         b.setText("Hello");
>>>     });
>>> });
>>> 
>>> This approach doesn't generate the mass of anonymous inner classes that
>>> the other double-brace approach does, but accomplishes the same basic job
>>> of using builders for (more or less) declarative construction of a
>>> container-hierarchy in code.
>>> 
>>> So this is the long and the short of it. Eva can follow up with an
>>> in-depth post about the binary compatibility concerns if you wish. My
>>> proposal after having weighed the options is to phase out the Builders by
>>> deprecating them in 8 and removing them from the class path in 9. I believe
>>> that FXML or Lambda's or alternative languages all provide other avenues
>>> for achieving the same goals as builders but without the additional cost in
>>> byte codes or classes.
>>> 
>>> We have some cases where SceneBuilder is presently relying on the
>>> Builders, and we'd have to provide alternatives for that functionality it
>>> is presently using (such as annotations for constructor arguments).
>>> 
>>> Richard
> 
> 



More information about the openjfx-dev mailing list