Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 Interface Inheritance
Index -> Programming, Ruby -> Ruby Tutorials
View previous topic Printable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
wtd




PostPosted: 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
Sponsor
sponsor
Cervantes




PostPosted: Tue Aug 23, 2005 8:04 am   Post subject: (No subject)

Thanks wtd! Very Happy

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? Very Happy
rizzix




PostPosted: Tue Aug 23, 2005 12:27 pm   Post subject: (No subject)

what in the world is mix-ins ??? Smile
wtd




PostPosted: Tue Aug 23, 2005 1:59 pm   Post subject: (No subject)

rizzix wrote:
what in the world is mix-ins ??? Smile


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. Smile
Cervantes




PostPosted: 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. Eh
wtd




PostPosted: 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. Smile
rizzix




PostPosted: 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




PostPosted: 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
Sponsor
sponsor
Display posts from previous:   
   Index -> Programming, Ruby -> Ruby Tutorials
View previous topic Tell A FriendPrintable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 1  [ 8 Posts ]
Jump to:   


Style:  
Search: