"If it walks into a forest and quacks but there's nobody there to hear it, is it a duck?"
The problem isn't with duck typing, per se, but in testing the duckiness of an object. Consider a foo object that has a bar method, and a mumble object that also has a bar method. You can define a method that calls bar on an object that's passed in:
def call_bar(object)and foo or mumble can be passed in with no ensuing calamity. And happily,
object.bar
end
foo.respond_to? :barand
mumble.respond_to? :barreturn true as expected.
The real fun in Ruby starts when the bar method doesn't exist in an object, but is created by the method_missing method dynamically. In this scenario, the introspection part of duck-typing can break down. For instance:
class Fooblereturns false,
def method_missing(m)
Fooble.
class_eval "def #{m.id2name}() #{helper_for m} end"
self.instance_eval "#{m.id2name}"
end
end
fooble = Fooble.new
fooble.respond_to? :bar
call_bar(fooble)calls fooble's bar, which faults to method_missing, which creates the bar method in Fooble and returns whatever the code provided by helper_for(:bar) returns, and then
fooble.respond_to? :barreturns true!
This is Lame-Duck Typing. Duck Typing that may not be effective all of the time.
Granted, this is a bit contrived, but in such scenarios testing has to be equally contrived. What it amounts to is a logical race condition. In systems that can create code at run time, introspection can be questionable.
Be careful!
1 comment:
Don't check if it has a method. Call the method as if it exists and deal with the exception if it does not.
- Paddy.
Post a Comment