Sue is designing a JavaScript library, Magician.js
. Its linchpin is a function which pulls a Rabbit
out of the argument passed.
She knows its users may want to pull a rabbit out of a String
, a Number
, a Function
, perhaps even a HTMLElement
. With that in mind, she could design her API like so:
The strict interface
Magician.pullRabbitOutOfString = function(str) //...
Magician.pullRabbitOutOfHTMLElement = function(htmlEl) //...
Each function in the above example would know how to handle the argument of the type specified in the function name/parameter name.
Or, she could design it like so:
The "ad hoc" interface
Magician.pullRabbit = function(anything) //...
pullRabbit
would have to account for the variety of different expected types that the anything
argument could be, as well as (of course) an unexpected type:
Magician.pullRabbit = function(anything) {
if (anything === undefined) {
return new Rabbit(); // out of thin air
} else if (isString(anything)) {
// more
} else if (isNumber(anything)) {
// more
}
// etc.
};
The former (strict) seems more explicit, perhaps safer, and perhaps more performant — as there are little or no overheads for type checking or type conversion. But the latter (ad hoc) feels simpler looking at it from the outside, in that it "just works" with whatever argument the API consumer finds convenient to pass to it.
For the answer to this question, I'd like to see specific pros and cons to either approach (or to a different approach altogether, if neither is ideal), that Sue should know which approach to take when designing her library's API.
Best Answer
Some pros and cons
Pros for polymorphic:
Pros for ad-hoc:
Cat
instance? Would that just work? if not, what is the behavior? If I don't limit the type here, I have to do so in the documentation, or in the tests which might make a worse contract.pullRabbitOutOfString
version is likely to be much faster in engines like V8. See this video for more information. Edit: I wrote a perf myself, it turns out that in practice, this is not always the case.Some alternative solutions:
In my opinion, this sort of design isn't very 'Java-Scripty' to begin with. JavaScript is a different language with different idioms from languages like C#, Java or Python. These idioms originate in years of developers trying to understand the language's weak and strong parts, what I'd do is try to stick with these idioms.
There are two nice solutions I can think of:
Solution 1: Elevating Objects
One common solution to this problem, is to 'elevate' objects with the ability to have rabbits pulled out of them.
That is, have a function that takes some type of object, and adds pulling out of a hat for it. Something like:
I can make such
makePullable
functions for other objects, I could create amakePullableString
, etc. I'm defining the conversion on each type. However, after I elevated my objects, I have no type to use them in a generic way. An interface in JavaScript is determined by a duck typing, if it has apullOfHat
method I can pull it with the Magician's method.Then Magician could do:
Elevating objects, using some sort of mixin pattern seems like the more JS thing to do. (Note this is problematic with value types in the language which are string, number, null, undefined and boolean, but they're all box-able)
Here is an example of what such code might look like
Solution 2: Strategy Pattern
When discussing this question in the JS chat room in StackOverflow my friend phenomnomnominal suggested the use of the Strategy pattern.
This would allow you to add the abilities to pull rabbits out of various objects at run time, and would create very JavaScript'y code. A magician can learn how to pull objects of different types out of hats, and it pulls them based on that knowledge.
Here is how this might look in CoffeeScript:
You can see the equivalent JS code here.
This way, you benefit from both worlds, the action of how to pull isn't tightly coupled to either the objects, or the Magician and I think this makes for a very nice solution.
Usage would be something like: