Interface Inheritance
Author |
Message |
wtd
|
Posted: Tue Aug 23, 2005 3:38 am Post subject: Interface Inheritance |
|
|
In many object-oriented languages interface inheritance is a key aspect of the typing system. We can use interfaces to allow classes which don't share a meaningful common ancestor to be passed to the same method.
For instance, we may have the CarnivalWorker and Dog classes. These two share no classic inheritance relationship. However, both can bark.
Let's look at some C# code.
code: | using System;
interface IBarker
{
public void Bark();
}
class CarnivalWorker : IBarker
{
public void Bark()
{
Console.WriteLine("Step right up!");
}
}
class Dog : IBarker
{
public void Bark()
{
Console.WriteLine("Woof!");
}
}
public class Test
{
public static void Main(string[] args)
{
Dog fido = new Dog();
CarnivalWorker crazyBobTheCarnie = new CarnivalWorker();
Bark(fido);
Bark(crazyBobTheCarnie);
}
void Bark(IBarker barkingObject)
{
barkingObject.Bark();
}
} |
But in Ruby we don't impose such limitations of method arguments, so there's no need for this, right?
Well, strictly speaking, no, but it can be very handy to know this relationship. So, how can we implement interfaces?
Well, we'll use modules. Normally modules are used to compartmentalize methods. The most common example is the enumerable module which provides all kinds of handy looping methods.
The methods in a module do have an implementation, and not just a signature. However, we can then override this implementation to provide the functionality we want. The default implementation can be to throw a meaningful error message.
code: | module Barker
def bark
raise NoMethodError, "#{self.class} does not implement the bark method."
end
end
class CarnivalWorker
include Barker
def bark
puts "Step right up!"
end
end
class Dog
include Barker
def bark
puts "Woof!"
end
end
def bark(barking_object)
barking_object.bark
end
fido = Dog.new
bob_the_crazy_carnie = CarnivalWorker.new
bark(fido)
bark(bob_the_crazy_carnie) |
We can see the inheritance relationship at the interactive interpreter easily enough.
code: | irb(main):041:0> fido.is_a? Barker
=> true
irb(main):042:0> bob_the_crazy_carnie.is_a? Barker
=> true |
We can also see what happens if we have a class that includes the Barker method, but doesn't override the method.
code: | class Foo
include Barker
end |
code: | irb(main):053:0> bark(Foo.new)
NoMethodError: Foo does not implement the bark method.
from (irb):15:in `bark'
from (irb):36:in `bark'
from (irb):53
irb(main):054:0> |
|
|
|
|
|
|
Sponsor Sponsor
|
|
|
Cervantes
|
Posted: Tue Aug 23, 2005 8:04 am Post subject: (No subject) |
|
|
Thanks wtd!
I thought you said though that Ruby uses mix-ins.
wtd wrote: Ruby and Java only support multiple inheritance via mix-ins and interface inheritance, respectively.
Or is that backwards? If not, would you so kindly explain these as well? |
|
|
|
|
|
rizzix
|
Posted: Tue Aug 23, 2005 12:27 pm Post subject: (No subject) |
|
|
what in the world is mix-ins ??? |
|
|
|
|
|
wtd
|
Posted: Tue Aug 23, 2005 1:59 pm Post subject: (No subject) |
|
|
rizzix wrote: what in the world is mix-ins ???
A very powerful way of programming. The most powerful, mix-in, in my opinion, is Enumerable.
Essentially, with a mix-iin you look at a chunk of methods that are common to many classes. So far this is like interface inheritance.
However, in this case we provide implementations. In order to do this, we need to figure out what basic functionality each class that includs the mix-in must implement. In the case of enumerable, it's the "each" method.
Let's look at a disturbingly simple example.
code: | irb(main):001:0> class Foo
irb(main):002:1> def each
irb(main):003:2> for x in 1..4
irb(main):004:3> yield x
irb(main):005:3> end
irb(main):006:2> end
irb(main):007:1> end
=> nil
irb(main):008:0>
irb(main):009:0* Foo.new.each { |x| puts x }
1
2
3
4
=> 1..4
irb(main):010:0> class Foo
irb(main):011:1> include Enumerable
irb(main):012:1> end
=> Foo
irb(main):013:0> Foo.new.collect { |x| "#{x}!" }
=> ["1!", "2!", "3!", "4!"]
irb(main):014:0> Foo.new.to_a
=> [1, 2, 3, 4]
irb(main):015:0> Foo.new.select { |x| x % 2 == 0 }
=> [2, 4]
irb(main):016:0> Foo.new.reject { |x| x % 2 == 0 }
=> [1, 3] |
Now, I didn't write any code for collect, to_a, select, or reject, but I'm using them anyway. |
|
|
|
|
|
Cervantes
|
Posted: Tue Aug 23, 2005 3:51 pm Post subject: (No subject) |
|
|
I must be missing something, because I don't see how that is any different than the code in the first post. Both have a module which is included into the class, thus allowing methods from the module to be integrated into the class. But the top post is supposedly Interface Inheritance, whereas this one is a mix-in?
I pictured a mix-in as allowing the programmer to choose which methods to pull from a module. |
|
|
|
|
|
wtd
|
Posted: Tue Aug 23, 2005 4:18 pm Post subject: (No subject) |
|
|
The first is a mix-in, but by having the default implementation be to raise an exception, we get an exception anytime we try to use one of those methods in a class that includes the module but doesn't override the method. |
|
|
|
|
|
rizzix
|
Posted: Tue Aug 23, 2005 4:47 pm Post subject: (No subject) |
|
|
i see problems if u include more than one module having the same method name -,- |
|
|
|
|
|
wtd
|
Posted: Tue Aug 23, 2005 5:05 pm Post subject: (No subject) |
|
|
This is reasonably easy to understand. Methods can be changed at any time.
code: | irb(main):019:0> def hello
irb(main):020:1> "yo"
irb(main):021:1> end
=> nil
irb(main):022:0> hello
=> "yo"
irb(main):023:0> def hello
irb(main):024:1> "oy"
irb(main):025:1> end
=> nil
irb(main):026:0> hello
=> "oy"
irb(main):027:0> |
But we can keep the old method around with "alias".
code: | irb(main):027:0> alias :old_hello :hello
=> nil
irb(main):028:0> def hello
irb(main):029:1> "foo"
irb(main):030:1> end
=> nil
irb(main):031:0> hello
=> "foo"
irb(main):032:0> old_hello
=> "oy"
irb(main):033:0> |
Naturally, this extends to working with modules.
code: | $ irb
irb(main):001:0> module Foo
irb(main):002:1> def baz() "Hello" end
irb(main):003:1> end
=> nil
irb(main):004:0> module Bar
irb(main):005:1> def baz() puts "yo" end
irb(main):006:1> end
=> nil
irb(main):007:0> class Qux
irb(main):008:1> include Foo
irb(main):009:1> include Bar
irb(main):010:1> end
=> Qux
irb(main):011:0> Qux.new.baz
yo
=> nil
irb(main):012:0> class Wooble
irb(main):013:1> include Foo
irb(main):014:1> alias :foo_baz :baz
irb(main):015:1> include Bar
irb(main):016:1> end
=> Wooble
irb(main):017:0> Wooble.new.foo_baz
=> "Hello"
irb(main):018:0> Wooble.new.baz
yo
=> nil
irb(main):019:0> |
|
|
|
|
|
|
Sponsor Sponsor
|
|
|
|
|