John Carter wrote:
Duck typing is an even freer form of polymorphism and in the case of the
example I mentioned, with Duck Typing, the geometry library would work
on anything that responded to +,-,*,/,==,<, including Vectors and
Matrices!I’m hinting at a prime rule of polymorphic / duck typed design. Don’t
gratuitously constrain the interface. Reusable means you don’t know what
other types may be fed to you in future, and to be reusable you must
allow it.
This is a good guideline for basic types like numbers, strings, arrays,
etc., because people do tend to create or reuse “similar types” (e.g.
DBI::Row or whatever it is, instead of Hash) which should “just work” with
your library.
But as you move away from basic types, through to middle-ground types,
like an XML document representation, to domain types, like a customer, the
balance changes. The prime rule you mention above becomes less and less
important.
If I’m writing some sort of CRM library that I want to share with the
world and have reused, then I should obviously document my interface well,
and not be overly strict about what I accept. Even so, it’s very unlikely
that someone else is going to have a class they’re using that’s
similar-to-but-not-quite my OpenCRM::Customer class. The benefits of a
duck typing attitude are not likely to take root in this system. But I’m
creating a library that is aimed at reuse, so I should allow for reuse.
Now, one step further: an internal application that is never going to
leave company walls. A system (involving customers, invoices, and
payments, say) that is entirely self-contained, and not aimed at reuse at
all. In this case, my system is designed to work with XYZ::Customer
objects, XYZ::Invoice objects, and XYZ::Payment objects. Furthermore, I
may reasonably use some class-checking code at strategic points. This is
not because I feel unsafe without it; it’s to encode the practical
limitations of the software. A line like
expect(customer, XYZ::Customer, payment, XYZ::Payment)
is executable documentation. It reminds you at a glance what you’re
dealing with. It gives you some context, some knowledge to build on when
you’re editing that method after a few months away from it. And it’s
documentation that doesn’t go out of date. The fact is, I’m not
needlessly restricting the interface (in any practical sense) by including
the above line. The interface was already restricted because of the
special-purpose nature of the system. (That line is using the
‘strongtyping’ library, BTW.)
The reason I canvas these examples is to emphasise that isolation (i.e.
not planning for reusability) is not necessarily a software sin. I agree
with the spirit(s) of the duck-typing discussions, but don’t want some
impressionable lurker thinking “Hey, I’m not considering reuse; am I
overly restricting the interface?; am I using Ruby correctly?” etc.
There’s a spectrum of reusability, and the onus is on the programmer to
work out where they are aiming and to use the appropriate techniques.
That’s a blessing and a curse. In other languages, you’re forced to
handle all typing issues in a certain way, which is restrictive, but at
least you don’t have to think. Personally, I’d rather think.
There’s a time and place for everything in Ruby
Cheers,
Gavin