Lately I've been following an approach that I suppose you could call "strict duck-checking in danger zones." I define a very lightweight "contract checker" and drop it into the beginning of methods that make a lot of assumptions about their arguments. For example, in a ruby app that wants certain keys in the dictionary you give to it:
class MethodContractException < StandardError
end
class Expecter
def initialize(suspect)
@suspect = suspect
end
def has_all_keys(keys)
missing_keys = keys.reject { |key| @suspect.has_key? key }
if !missing_keys.empty?
raise MethodContractException, "Method contract violated: "\
"#{@suspect.keys.inspect} should contain "\
"#{missing_keys.inspect}."
end
true
end
end
def expect(suspect)
Expecter.new(suspect)
end
class Widget
REQUIRED_SPECS = [:splines, :cogs, :thingamabobs]
def reticulate(dct_spec)
expect(dct_spec).has_all_keys(REQUIRED_SPECS)
@dct_spec = dct_spec
process(dct_spec)
end
end
Though, it may be better to encapsulate this kind of contract by creating a WidgetSpec class that demands each spec in the form of arguments to its constructor, and simply checking that the argument supplied to Widget::reticulate() is an instance of WidgetSpec. That is probably one of the few instances where instance checking is sensible; in other situations, using respond_to?() is preferable, because we are only concerned as to whether the object we are given can :quack, not that it is an instance of a Duck.
For everything else, I try to follow Joel Spolsky's advice on making wrong code look wrong: http://www.joelonsoftware.com/articles/Wrong.html