https://github.com/apache/wicket/pull/211 :-)
Let's drop them all!

Martin Grigorov
Wicket Training and Consulting
https://twitter.com/mtgrigorov

On Wed, Feb 8, 2017 at 4:34 PM, Emond Papegaaij <[email protected]>
wrote:

> I can live with the following:
>
> Factory methodes should:
>  * accept exactly 1 lambda (and possibly an id)
>  * be a static method on the component or behavior they create
>  * be given meaningfull names
>  * be limited to at most 1 or 2 per type
>  * not pass the instance they create to the lambda
>
> This would mean all methods should be moved from Lambdas and duplicates
> like
> AjaxLink.onClick should be removed.
>
> Still, I like the idea to remove them all better. We can also move them
> all to
> a factory class and place this class in experimental. That way we can do
> with
> it whatever we like.
>
> Best regards,
> Emond
>
> On woensdag 8 februari 2017 16:20:59 CET Andrea Del Bene wrote:
> > I agree with Martijn, especially when he warns about the huge risk we
> take
> > overusing lambdas and making API inconsistent. But I don't agree to take
> > such an extreme choice like removing EVERY factory methods. Like Martin
> > suggested these methods can be very useful when they are limited to the
> > very basic functionality of a component (link onClick for a Link).
> > More in general I would say that a lambda factory method should not take
> > more than one lambda as parameter.
> >
> > On Wed, Feb 8, 2017 at 3:17 PM, Martin Grigorov <[email protected]>
> >
> > wrote:
> > > On Wed, Feb 8, 2017 at 3:08 PM, Martijn Dashorst <
> > > [email protected]
> > >
> > > > wrote:
> > > >
> > > > There are many things to consider when designing an API. Usability of
> > > > the API, readability of the resulting code, maintenance burden for
> the
> > > > core developers, but also performance, memory consumption and
> > > > serialization overhead.
> > > >
> > > > Adding lifecycle fields to Component for any extension point will
> > > > dramatically increase the footprint of any Wicket application.
> > > > Lambda's are not a free lunch and add considerable memory and
> > > > serialization overhead to Java code.
> > > >
> > > > Wicket is not only used for small demos of how fancy lambda's can be,
> > > > but also for applications where 10's of thousands or even millions of
> > > > users concurrently use the application. Adding a byte to the
> footprint
> > > > of Component can increase the memory footprint of the application
> with
> > > > 10's of megabytes.
> > > >
> > > > So yes we have considered adding these things but find the problem
> > > > they solve not worth the extra footprint. Furthermore the clarity of
> > > > the code does not increase when you have 3 or 4 methods that need
> > > > implementing, but those add considerable memory footprint (8 bytes
> per
> > > > reference when unused, much more when used) when provided through
> > > > lambdas.
> > > >
> > > > Calculation
> > > >
> > > > Say you have 1 million components in memory. Say we add lambda's for
> > > > the primary life cycle events and enabled/visible flags. This is:
> > > >
> > > > - onInitialize
> > > > - onEvent
> > > > - onReAdd
> > > > - onRemove
> > > > - onRender
> > > > - onConfigure
> > > > - onBeforeRender
> > > > - onBeforeRenderChildren
> > > > - onComponentTag
> > > > - onComponentTagBody
> > > > - onAfterRender
> > > > - onAfterRenderChildren
> > > > - onDetach
> > > > - isVisible
> > > > - isEnabled
> > > >
> > > > This is without onFoo methods available in Link, Form etc. 15
> > > > references that may or may not be used. 15*8 = 120 bytes per
> > > > component. Now we have added 120MB to an application without anyone
> > > > even using the functionality. The cost is considerably higher when
> > > > actually using the lambda's. See the measurements I did for my talk
> at
> > > > ApacheCon 2016 in Sevilla [1]
> > > >
> > > > Of course we can try to minimize the memory footprint, and store the
> > > > event references in the data bucket of Component and store only those
> > > > events that are actually used. But this will add an O(n^2) for every
> > > > request because we need to loop through the data array at each life
> > > > cycle event to find if there's a registered reference, and if so use
> > > > that. While for a single request this is not too big an issue, it
> adds
> > > > up when you consider that this must be done for millions of
> components
> > > > for all lifecycle events.
> > > >
> > > > API design
> > > >
> > > > So let's consider the case when we were to add setters for lambda's
> to
> > > > Component et al. We can't remove the old way of subclassing because
> > > > that would break the world. Thus we have two ways of how to add an
> > > > onConfigure or onDetach event to a component. Which one is canon? We
> > > > already have at least 3 ways to determine whether a component is
> > > > visible or not:
> > > >
> > > > - call setVisible (static visibility)
> > > > - override isVisible
> > > > - setVisibilityAllowed
> > > > - override onConfigure() and call setVisible
> > > >
> > > > This has harmed the Wicket API in my opinion and is a result of us
> not
> > > > understanding at the time how, when and how often visibility needed
> to
> > > > be evaluated.
> > > >
> > > > Adding setters for lambda's would at least double the API surface.
> > > > Furthermore you need to take into account that these setters can't be
> > > > hidden by subclasses. So when you use a component that has
> onConfigure
> > > > overridden, what happens when you setOnConfigure()?
> > > >
> > > > public class FooPanel extends Panel {
> > > >
> > > >     public FooPanel(String id) { super(id); }
> > > >
> > > >     @Override
> > > >     protected void onConfigure() {
> > > >
> > > >         super.onConfigure();
> > > >         // if something set visiblity
> > > >
> > > >     }
> > > >
> > > > }
> > > >
> > > > add(new FooPanel("foo").setOnConfigure(() -> { ... do something
> > > > completely different ... });
> > > >
> > > > So which one should prevail? the lambda or the overridden method? Ah
> I
> > > > hear you say "rewrite the FooPanel such that it doesn't override
> > > > onConfigure". But then we have 2 onConfigure lambda's set:
> > > >
> > > > public class FooPanel extends Panel {
> > > >
> > > >     public FooPanel(String id) {
> > > >
> > > >         super(id);
> > > >         setOnConfigure(() -> {
> > > >
> > > >             /* if something set visibility */
> > > >
> > > >         });
> > > >
> > > >     }
> > > >
> > > > }
> > > >
> > > > add(new FooPanel("foo").setOnConfigure(() -> { ... do something
> > > > completely different ... });
> > > >
> > > > So which one should prevail? Well, call both! OK, this means that we
> > > > need to store both lambdas. So each lifecycle event lambda store
> needs
> > > > to be a data structure that can hold multiple lambdas, which
> increases
> > > > the memory footprint even further. Then we have to issue of which
> > > > lambda to call first. The one added in the constructor of FooPanel,
> or
> > > > the one that was set after construction?
> > > >
> > > > This doesn't even take into account the semantics of for example the
> > > > onInitialize lifecycle event, which states that it is called when a
> > > > component is added to the hierarchy of a page (i.e. a parent-path
> > > > exists to the page). A setOnInitialize can be invoked at any moment
> > > > after construction. Therefore its semantics become even muddier than
> > > > they already are:
> > > >
> > > > Label label = new Label(...);
> > > > add(label);
> > > >
> > > > label.setOnInitialize(() -> { ... });
> > > >
> > > > OK, so we don't add a lambda possibility for onInitialize (queue the
> > > > pull requests).
> > > >
> > > > Another thing to consider is that a component builder currently can
> > > > decide to close off particular events from being overridden or
> > > > modified. For example:
> > > >
> > > > public class FooPanel extends Panel {
> > > >
> > > >     public FooPanel(String id) { super(id); }
> > > >
> > > >     /** No longer overridable */
> > > >     @Override
> > > >     protected final void onConfigure() {
> > > >
> > > >         super.onConfigure();
> > > >         // if something set visiblity
> > > >
> > > >     }
> > > >
> > > > }
> > > >
> > > > The component designer found it necessary to remove the ability to
> > > > override onConfigure().
> > > >
> > > > However you can't remove the ability to add/set lambda's from
> > > > Component. These methods need to be public for them to be usable, and
> > > > you can't hide those methods.
> > > >
> > > > Summary
> > > >
> > > > Lambdas are a great tool but should be used wisely and we should be
> > > > careful about our API and take good design into consideration instead
> > > > of just papering our API with lambdas. There are many aspects to
> > > > consider, not just the fact that you can write 2 lines of
> > > > non-executable code less.
> > >
> > > YES!
> > > That's why I think the current factories we have is the best we can
> give -
> > > they are very handy for (my?!) the trivial case and they cannot be
> > > extended
> > > further with more functionality, it is just not practical to add more
> to
> > > them.
> > > For more inspiration take a look at other UI libs like
> JavaFX/TornadoFX,
> > > Vaadin 8, Scalatags/Kotlinx.html, ...
> > >
> > > > Martijn
> > > >
> > > > [1] http://www.slideshare.net/dashorst/whats-up-with-wicket-
> 8-and-java-8
> > > >
> > > > On Wed, Feb 8, 2017 at 11:45 AM, Andrew Geery <
> [email protected]>
> > > >
> > > > wrote:
> > > > > Rather than using static factory methods, would we ever consider
> > >
> > > pushing
> > >
> > > > > the lambdas into the component classes themselves?
> > > > >
> > > > > For example, if we did this with the Button class, the change would
> > > > > be:
> > > > >
> > > > > - Add two private fields, SerializableConsumer<Button>
> submitHandler,
> > > > > errorHandler
> > > > > - Add "setters" for these fields -- e.g., Button
> > > > > submitHandler(SerializableConsumer<Button> submitHandler)
> > > > > - Modify the onSubmit() and onError() methods to call the handler
> > >
> > > methods
> > >
> > > > > if they are not null
> > > > >
> > > > > A call would be something like:
> > > > >
> > > > > add(new Button("button").submitHandler(b ->
> > > >
> > > > System.out.println("clicked"));
> > > >
> > > > > Obviously, it is still possible to sub-class Button and override
> > > > > onSubmit(), so existing code will continue to work.  However, for
> code
> > > > > going forward, it should be much less needed and, in my opinion,
> much
> > > > > clearer.
> > > > >
> > > > > Another advantage to doing things this way is that sub-classes
> inherit
> > > > > these methods and there is no need to add static factory methods
> for
> > > >
> > > > every
> > > >
> > > > > sub-class (sub-classes of AjaxButton would be a better example of
> > >
> > > this).
> > >
> > > > > Thanks
> > > > > Andrew
> > > > >
> > > > > On Wed, Feb 8, 2017 at 3:42 AM, Martijn Dashorst <
> > > >
> > > > [email protected]
> > > >
> > > > >> wrote:
> > > > >>
> > > > >> It is that your trivial use case is not my trivial use case and
> that
> > > > >> we will end up with a 100,000 trivial use cases.
> > > > >>
> > > > >> And no, confusion is not the big issue (though for
> onsubmit+onerror
> > > > >> it
> > > > >> is) but creating a good API is hard. It takes time and
> understanding.
> > > > >> Minimizing lines of code is not the only metric for a good API.
> > > > >>
> > > > >> Just as using inheritance/annotations/generics for everything is
> bad,
> > > > >> introducing factory methods everywhere will not age well.
> > > > >>
> > > > >> These methods are trivial for anyone to implement and should we
> reach
> > > > >> the conclusion that we actually need the factory methods in core,
> it
> > > > >> is trivial to refactor them towards the Wicket supplied factories.
> > > > >>
> > > > >> Are the factory methods in the way? Yes they are, because once we
> add
> > > > >> them, we can't evolve them without adding many more (introduce
> bloat)
> > > > >> or having to wait until a new major release.
> > > > >>
> > > > >> Martijn
> > > > >>
> > > > >>
> > > > >>
> > > > >>
> > > > >> On Tue, Feb 7, 2017 at 11:40 PM, Martin Grigorov <
> > >
> > > [email protected]>
> > >
> > > > >> wrote:
> > > > >> > Hi,
> > > > >> >
> > > > >> > These methods are factories for the trivial use cases where
> nothing
> > > >
> > > > else
> > > >
> > > > >> > needs to be overridden but the action method (methods, when
> there
> > > > >> > is
> > > > >> > onError()). They do help to reduce the verbosity!
> > > > >> > There are plenty of those cases. You can see many usages in
> > > > >> > wicket-examples. I have used such methods from
> wicketstuff-scala in
> > > >
> > > > one
> > > >
> > > > >> of
> > > > >>
> > > > >> > my projects in the past and I use similar ones in a project with
> > > >
> > > > Kotlin
> > > >
> > > > >> now.
> > > > >>
> > > > >> > A builder that provides methods like onConfigure(),
> > >
> > > onComponentTag(),
> > >
> > > > >> > onDetach(), ... would look really strange!
> > > > >> > Who will use it instead of just creating a (inner) class ?!
> > > > >> >
> > > > >> > But if those factory methods confuse core developers then they
> will
> > >
> > > be
> > >
> > > > >> even
> > > > >>
> > > > >> > more confusing to normal users :-/
> > > > >> >
> > > > >> > 0 from me.
> > > > >> >
> > > > >> > Martin Grigorov
> > > > >> > Wicket Training and Consulting
> > > > >> > https://twitter.com/mtgrigorov
> > > > >> >
> > > > >> > On Tue, Feb 7, 2017 at 5:07 PM, Martijn Dashorst <
> > > > >>
> > > > >> [email protected]
> > > > >>
> > > > >> >> wrote:
> > > > >> >>
> > > > >> >> All,
> > > > >> >>
> > > > >> >> I want to remove all component factory methods that are added
> for
> > > > >>
> > > > >> lambda's.
> > > > >>
> > > > >> >> My reasoning is that:
> > > > >> >> - removing them in 8.x (x > 0) is impossible
> > > > >> >> - adding them in 8.x (x > 0) is trivial
> > > > >> >> - we currently don't have a way to know what good idioms are
> > > > >> >> - having these factories push (new) developers to use the wrong
> > >
> > > idiom
> > >
> > > > >> >> - factory methods don't allow for additional overriding, thus a
> > > > >> >> combinatorial explosion of API
> > > > >> >> - I tend to think that builders are better suited as component
> > > >
> > > > factories
> > > >
> > > > >> >> Because it is trivial to re-introduce these factories or their
> > > > >> >> replacements at a later time, I propose to remove them now and
> > >
> > > figure
> > >
> > > > >> >> out in our actual applications what works, and what doesn't. I
> > > > >> >> also
> > > > >> >> think that just doing the conversion from W7 to W8 isn't
> enough to
> > > > >> >> figure this out.
> > > > >> >>
> > > > >> >> Example 1:
> > > > >> >>
> > > > >> >> Button.java has two factory methods:
> > > > >> >>
> > > > >> >> Button#onSubmit(String, SerializableConsumer<Button>), and
> > > > >> >> Button#onSubmit(String, SerializableConsumer<Button>,
> > > > >> >> SerializableConsumer<Button>)
> > > > >> >>
> > > > >> >> It is not possible to see without resorting to parameter
> naming,
> > > > >> >> hovering etc to see which functionality is bound by which
> > >
> > > parameter.
> > >
> > > > I
> > > >
> > > > >> >> find the naming confusing: onSubmit and onSubmit.
> > > > >> >>
> > > > >> >> Example 2:
> > > > >> >>
> > > > >> >> Behavior.java has two factory methods:
> > > > >> >>
> > > > >> >> Behavior onTag(SerializableBiConsumer<Component,
> ComponentTag>)
> > > > >> >> Behavior onAttribute(String name,
> > > > >> >> SerializableFunction<String, CharSequence> onAttribute)
> > > > >> >>
> > > > >> >> These achieve basically the same functionality, but other life
> > >
> > > cycle
> > >
> > > > >> >> events of Behavior don't have factory methods. NOR should they.
> > > > >> >>
> > > > >> >> Example 3:
> > > > >> >>
> > > > >> >> Lambdas.java has many factory methods, most of which are better
> > > > >> >> implemented by just using an anonymous inner class. For
> example,
> > > > >> >> Lambdas.link: often times you need to override onconfigure or
> > > > >> >> oncomponenttag as well as onclick.
> > > > >> >>
> > > > >> >> Martijn
> > > > >>
> > > > >> --
> > >
> > > > >> Become a Wicket expert, learn from the best:
> > > http://wicketinaction.com
> > >
> > > > --
> > > > Become a Wicket expert, learn from the best:
> http://wicketinaction.com
>
>
>

Reply via email to