Proposal: Deprecate Builders
Richard Bair
richard.bair at oracle.com
Wed Apr 3 21:17:40 PDT 2013
On Apr 3, 2013, at 4:34 PM, John Hendrikx <hjohn at xs4all.nl> wrote:
> The second example shows the problem that always occurs when building object structures:
>> Group g = new Group(gg -> {
>> gg.getChildren().addAll(new Button(b -> {
>> b.setText("Hello");
>> });
>> });
> What names should the variables have? When constructing a deeply nested set of objects I rapidly run out of good names for my variables. Giving them long descriptive names is incredibly counter-productive when you have to call several methods on them (ie, centerPanelWestBorderListGroup.setText() gets cumbersome). Inevitable we end up with a mess of variables g1, g2, g3, button1, button2, etc…
It shouldn't be a problem for siblings, but you do get that problem with each level. So for example:
Group root = new Group(g -> {
g.getChildren().addAll(new Button(b -> { … }), new Button(b -> { … }));
});
> This exactly what Builders prevent and also what the subclass-initializer pattern prevents. These patterns allow for code to be moved around without having to worry about variable naming conflicts -- or having to worry about keeping variable names updated (let's add another panel around the centerPanel...)
>
> I'm curious however what generic problem you are running into. Long ago I wrote code that solved the casting and generics problems with the with-pattern. It basically looked like this:
>
> public final Button extends AbstractButton<Button> {
> // empty subclass, which only overrides self()
> protected Button self() {
> return this;
> }
> }
>
> public abstract AbstractButton<C> extends AbstractNode<C> {
> // contains the main code
> public C setText(String text) {
> ...
> return self();
> }
> }
>
> public abstract AbstractNode<C> {
> public C setColor(Color color) {
> ...
> return self();
> }
>
> protected abstract C self();
> }
>
> Now, the nice thing here is that the user can access setters accross classes without having to cast the result, and there's no generics in user code:
>
> Button b = new Button().setColor(RED).setText("A"); // Note that after Node#setColor() I can still call Button#setText().
>
> The disadvantage of course was the extra classes required (although the final classes are mostly empty, and only provide the self() method as Java has no mechanism to do this more directly) and of course it made creating new controls a bit more effort (have to extend an Abstract class, and provide a convience final class).
>
> I'm wondering if this is also affected by the bug aforementioned and fails on JDK8, or if this is a different trick…
No, I think this is OK on Java 8 and was an approach looked at (although we looked at it by having AbstractFooBuilder & FooBuilder at every level, rather than in the actual class hierarchy). It suffers as you say from excessive classes. The other thing that bothers me is having a setter that doesn't return void. That doesn't bother me in the general sense (it seems like a fine thing to do!) but from the perspective that it breaks the conventional definition of a bean setter, and so will run afoul of any existing JavaBeans tools which might, for example, explicitly check for a public void setFoo method for any given foo property to determine whether it is writable. Fitting in to the existing Java universe was an important design constraint.
Richard
More information about the openjfx-dev
mailing list