Do we want to do a similar thing for factory methods around models, such as
LoadableDetachableModel#of?

Andrew

On Wed, Feb 8, 2017 at 2:53 PM, Andrea Del Bene <[email protected]>
wrote:

> I think we all (more or less :-) ) agree about moving factory methods away
> from components. I don't think we need a dedicated and experimental module
> for them. I'd rather move them to class Lamdas and, if we need it, we can
> deprecate them in future.
>
>
>
> On 08/02/2017 17:24, Sven Meier wrote:
>
>> I like Edmond's suggestion:
>>
>> "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."
>>
>> So we have a place to refer questioners to when they are looking for
>> these factory methods.
>>
>> Regards
>> Sven
>>
>>
>> On 08.02.2017 16:55, Martin Grigorov wrote:
>>
>>> 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