Hi all,
in order to clarify my understanding of the issue at hand I tried to
summarize the discussion. Comments are welcome.
Interfaces in Ruby - the short story.
There are none.
Ok, since this was quite short, we’ll try a bit harder.
Interfaces in Ruby - the real story.
Technically speaking Ruby does not need interfaces since it is dynamically
typed. Any argument list that at least satisfies the arity condition can be
handed into a method.
Since people appreciate the usage of interfaces and even more Design by
Contract (DbC) some mechanics were developed that mimic interfaces and
certain aspects of DbC. I list them in order of increasing complexity.
(1) Document which methods a method argument must implement or its expected
type.
(2) Define a module containing the methods that all raise an exception like
this:
module FooInterface
def bar(a,b) raise “bar(a,b) must be overridden”; end
end
class FooClass
include FooInterface
def bar(a,b) a+b; end
end
(3) Create a framework (like the one Paul Brannan suggested here [1]) for
defining interface methods and for checking that a particular instance
implements these methods.
(4) Try to support even more of DbC by dealing with preconditions and
postconditions like I tried in [2].
However: these things tend to get complicated and ugly, which seems to be
quite un-rubyish. Apart from that, they never reach the robustness and
power of languages with such features built in (like Eiffel).
From the perspective of an Eiffel developer this might not be satisfactory.
But then, one should keep in mind the different focus of Ruby. OO
capabilities are still a lot better than in Perl.
Regards
robert
[1]
http://rm-f.net/~cout/code/ruby/treasures/RubyTreasures-0.3/lib/hacks/interf
ace.rb.html
[2] Imperfect DbC experiments:
class ConditionException < Exception; end
class PreConditionException < ConditionException; end
class PostConditionException < ConditionException; end
module Contract
def type_check(value, type)
raise TypeError, “#{value} is not #{type}” unless value.kind_of? type
end
def pre_condition_check(binding, *predicates)
predicates.each do |p|
raise PreConditionException, “violation of ‘#{p.strip}’” unless
eval(p, binding)
end
end
def post_condition_check(binding, *predicates)
predicates.each do |p|
raise PostConditionException, “violation of ‘#{p.strip}’” unless
eval(p, binding)
end
end
end
module FooContract
include Contract
def bar(a,b)
# check preconditions
env = binding()
type_check(a, String)
type_check(b, Integer)
pre_condition_check env, %{ a.length < 10 }, %{ a.length > 0 }
# invoke method
result = barImpl(a,b)
# check postconditions
env = binding()
type_check(result, Array)
post_condition_check env, %{ result.size == 3 }
# finished
return result
end
end
class FooImpl
include FooContract
protected
def barImpl(a,b) [a, b, a*b]; end
end
foo = FooImpl.new
f.bar(“x”,3)
begin
f.bar(“x”,“3”)
rescue Exception => e
p e
end
begin
f.bar(“xxxxxxxxxxx”,3)
rescue Exception => e
p e
end
begin
f.bar("",3)
rescue Exception => e
p e
end