Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple-Inheritance type class inheritance in ruby

I got a Base superclass and a bunch of derived classes, like Base::Number, Base::Color. I'd like to be able to use those child classes as if I they inherited from say Fixnum in the case of Number.

What's the best way to do this, while still having them respond appropriately to is_a? Base ?

So, I should be able to do

Number.new(5) + Number.new(6) # => 11
Number.new.is_a? Base         # => true

I'm thinking I could mix-in Base, and overwrite the is_a?, kind_of? and instance_of? methods, but hopefully there's a cleaner way.

like image 334
cloudhead Avatar asked Jul 07 '09 22:07

cloudhead


4 Answers

This is actually quite simple using Ruby:

module Slugish
  attr_accessor :slug
  def loud_slug
    "#{slug}!"
  end
end

class Stringy < String
  include Slugish
end

class Hashy < Hash
  include Slugish
end

hello = Stringy.new("Hello")
world = Stringy.new("World")

hello.slug = "So slow"
world.slug = "Worldly"

hello.loud_slug      #=> "So slow!"
world.loud_slug      #=> "Worldly!"

hello.is_a?(Slugish) #=> true
world.is_a?(Slugish) #=> true

"#{hello} #{world}"  #=> "Hello World"

stuff = Hashy.new
stuff[:hello] = :world
stuff.slug = "My stuff"
stuff.loud_stug      #=> "My stuff!"
stuff.is_a?(Slugish) #=> true
like image 179
Yehuda Katz Avatar answered Nov 15 '22 05:11

Yehuda Katz


Why do you insist on using is_a?/kind_of? when respond_to? is a much cleaner way of checking things? You want objects to implement an interface/contract not to be a subclass of any arbitrarily chosen superclass. But maybe I'm missing some kind of requirement here.

Edit: I understand your reasoning, but it often leads to poor OO/dynamic design. Either you're doing something like this, which might be an acceptable idea in leaf classes but in a framework should be solved with inheritance:

if a.is_a?(something)
   #do something
elif a.is_a?(something_else)
   #do something else
...

or something like this:

if !a.is_a?(something)
   #raise condition/return null/etc.
endif 
...

I think that letting code fail with does not understand exception in message passing based language is a perfect design decision.

As an added problem, using is_a? instead of respond_to? limits your ability to use mock-up objects while unit testing. Which can be quite a big issue even for moderately complicated code.

like image 44
Wojciech Bederski Avatar answered Nov 15 '22 07:11

Wojciech Bederski


I think you are using inheritance incorrectly if you have completely unrelated classes like "Number" and "Color" all deriving from the same base class. I would use composition instead if they do need access to the same routines (not sure why they would though).

like image 31
Ed S. Avatar answered Nov 15 '22 07:11

Ed S.


Ruby's equivalent to multiple inheritance is mixins. It sounds to me like what you want is for Base to be a module that gets mixed in to several classes.

like image 21
Chuck Avatar answered Nov 15 '22 05:11

Chuck