jQuery Overloading Strategy -- What Not To Do

S

Scott Sauyet

dhtml said:
Gotcha. No, I don't think overloading's always bad.

Okay, I'd love to hear other people's ideas as well on this.

I try to minimize complexity in methods. Conditionals and loops add
complexity, too but sometimes it makes sense to use them.

The distinction I've been trying to make, though, is between
complexities within the implementation and complexities within the
interface presented. When there's a tradeoff to be made between the
two, I almost always will choose to keep the interface complexity
low. Of course it's better if neither is complex, but still there is
often a choice to be made between these.

    Invoice.prototype.addLineItems = function(items) {
      var myItems = this.items;
      items = isArray(items) ? items : [items];
      each(items, function(item) {
        myItems.push(item);
      });
    };
This is a form of overloading.  The function can take a single
LineItem parameter or an array of LineItems.  To me, since the systems
that feeds me does not always distinguish between single items and
arrays of them, it's much cleaner to have the isArray check, and hence
the overloading, in this function.  

That's what `Array.prototype.concat` does. So you can get rid of the
entire function and just use `concat`. Like so:

| myItems.concat(items);

I can't win. :-(

I swear, if it weren't for a non-disclosure agreement, I'd just post
some actual code from my current project. pretend instead of

| myItems.push(item);


I wrote

| doSomethingDifficultThatDoesntHaveAnEquivalentNativeOneLiner(item)

:)

I'm taking the Worse is Worse side in the classic Richard Gabriel
debate.  [1]

Haha, yes -  I like that "less is more" stuff. And knowing how and
when to say "no" when asked to provide spurious features.

That latter is something I struggle with far too much. It's hard when
those asking for the features are the ones signing the checks! :)

-- Scott
 
D

dhtml

Okay, I'd love to hear other people's ideas as well on this.
Code examples would help.
The distinction I've been trying to make, though, is between
complexities within the implementation and complexities within the
interface presented.  When there's a tradeoff to be made between the
two, I almost always will choose to keep the interface complexity
low.  Of course it's better if neither is complex, but still there is
often a choice to be made between these.
I disagree.

[...]
Actually:
| this.items = myItems.concat(items);
I can't win.  :-(
Sorry that didn't help.
I swear, if it weren't for a non-disclosure agreement, I'd just post
some actual code from my current project.  pretend instead of

|  myItems.push(item);

I wrote

|  doSomethingDifficultThatDoesntHaveAnEquivalentNativeOneLiner(item)
OK.

Remove the isArray check and use typeof items == "string". Simpler,
faster, safer. With mindset, you may find that you can remove more
dependencies than just isArray.
 
D

dhtml

dhtml wrote: [...]
Now on that factory pattern,  I remember teh last time I showed you
that pattern that you thought it was too complicated.  [ ... ]

Really, I don't remember that?  I know you discussed this pattern when
we talked about APE, but I don't remember making this critique at all.
OK, sorry. Whatever the initial motivating factor was, I added
DelegateFactory.

Using a DelegateFactory based widget requires no boilerplate. The
tricky part is creating the DelegateFactory I'd like to simplify that
process.
 
D

dhtml

Do you think all overloading in JS is a problem?  I agree that many of
jQuery's overloadings are absurd, most prominently its main function
`jQuery`.  But I haven't found anything really wrong with the dual get/
set nature of a number of its functions.  While I don't object to
`getVal/setVal` a single function `val` also seems all right.  Do you
object?

It becomes a problem when those writing the scripts don't understand
the underlying behaviour of browsers. There are many questions on
stackoverflow about fundamental browser behaviour, such as is it OK to
just use selectElement.value rather than
selectElement.options[selectElement.selectedIndex].value, that get
responses like "just use jQuery: $(#selectElementId).val()".

Which not only doesn't answer the OP's question, but actively
discourages them from asking in the first place. A full explanation
would include getting the value of a select element depending on the
markup with links to relevant standards and the well known quirks in
commonly used browsers. I would have thought that a full explanation
is a better argument for using a library than a simplistic "just use
it".

Browsers are slowing converging on support for standards, and
standards themselves are changing to provide more explicit support for
what developers want to do (forget what users actually want, it's all
about the developers, isn't it?). It will not be that long before
behaviour will be sufficiently standard and basic functionality
sufficiently complete that 3rd party libraries will not be required
for most things. For example, script-based CSS selector engines are
redundant in browsers with querySelector/All support, many transitions
can now be performed purely in CSS, no doubt simple animations will
follow.
Implementations need to be careful with implementing new features. It
may seem tempting to release a new feature (say something in HTML5)
but if that feature is buggy, it means that users have to deal with
those bugs and web developers have to do deeper capability checks. I'm
thinking now of ES5 features that got shipped buggy and HTML5 input
type=date, which is a mess in WebKit.

Spec developers need to keep degradation strategies in mind so that
when developers use the feature, they can provide a graceful
degradation path for implementations that lack that new feature.


On specs and getting back to Overloading, here is an example of
problematic overloading:
| For the DOM we have this idea of having a method that
| can conveniently create an element, set its attributes, event
| handlers, and child nodes. However, to keep the method
| convenient some of those need to become optional and therefore
| it requires some kind of overloading, which is
| where things become problematic.
| Maybe there is a better design, but so far we have something
| like this:
|
| create(name)
| create(name, attributes)
| create(name, children)
| create(name, attributes, children)
Here, `children` follows `attributes`.

| create(name, attributes, eventhandlers, children)
But now `children` follows `eventhandlers`.

|
| name is a string.
|
| attributes is an object.
|
| eventhandlers is an object.
|
| children is either a Node or string, or an array of Nodes and
strings.

The typechecking issue is not the problem; it is another step down the
wrong path. The wrong path is defining the "create" to be so
complicated that it defies descriptive name. The intent of "create" is
to do number of various things, using various parameters provided in
unmemorable and variant order.

http://lists.w3.org/Archives/Public/public-script-coord/2011JulSep/0430.html
 
D

dhtml

Do you disagree with my choice (to keep the complexity in the
implementation and not the interface)?  Or with the idea that there is
sometimes a tradeoff to be made?
I was disagreeing with the choices coming down to a tradeoff. Now
you're saying sometimes a tradeoff must be made; I agree with
sometimes, though perhaps not for the same situations you're thinking
of.

Your revamped item/items overloading added a tiny bit of complexity
with a ternary statement. That not seem like a problem to me.

Another example of overloading with host objects:
`jQuery.isPlainObject`, added long after reviews of jQuery problems
caused by their use of overloading. Somehow those reviews didn't stop
`isPlainObject` from being created and depended on to discriminate
between any type of value, including null, undefined, host object,
native object, both internally within the core of jQuery and
externally for the whole world.

That function fails for the reasons pointed out here long ago (see
"JQuery button problem" and "congrats to jQuery"). Essentially it
boils down to not defining the intention, being too generalized, then
imparting false expectations on what the browser should do.

With `isPlainObject` the false expectation is expecting a specific,
non-standard enumeration order. They expect that across all
implementations and all objects (including host objects) and then when
it doesn't do what they want, by filing that (yet another) false
expectation as a bug. Since enumeration order is nonstandard, of
course method can be expected to fail (and it does). (And anyone using
or considering jQuery should look carefully at those threads and also
http://bugs.jquery.com/ticket/9507).

We've covered cases of overloading that work and are justifiable. We
have also seen cases that can't work (by any reasonable expectations)
and don't work. We haven't yet debated cases where overloading can
work but adds more complexity than it is worth.

Complicated methods invite more bugs and are harder to debug, so why
add them?

Generally desirable: Clear implementation code. If that code uses
other defined objects then those objects should have clear interfaces
and well-defined methods. Closures can be used to hide details and
keep the interface simple.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,075
Messages
2,570,545
Members
47,197
Latest member
NDTShavonn

Latest Threads

Top