Proposal: Deprecate Builders

Tom Eugelink tbee at tbee.org
Mon Mar 25 13:09:09 PDT 2013


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