views:

646

answers:

5

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.

+1  A: 

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).

Ed Swangren
the point is they aren't unrelated, they have shared functionality and I need them all to be recognized as Base, vs other ruby data types.
cloudhead
Numbers and colors are indeed unrelated. Inheritance is a "is-a" relationship, not a "has-a". Use composition.
Ed Swangren
Inheritance is not about bundling together various helper functions.
Ed Swangren
and String and Fixnum are also unrelated, yet they both inherit from Object. how weird!
cloudhead
Everything inherits from Object because everything is-a object. You obviously don't understand when to use inheritance and when to use composition, so I would suggest finding a beginner's tutorial on object oriented programming. I would tell you what the difference is were you not so ignorant about getting another opinion.
Ed Swangren
Well same here, my classes are all types of Node::Base, part of a tree structure. Doesn't matter anyway, I found what I needed with Forwardable: it'll allow me to delegate certain methods to an instance variable.People should not downvote a question just because they aren't good enough to answer it. What a ghetto.
cloudhead
You didn't say that though, so how were we supposed to know?
Ed Swangren
I said: "got a base superclass, and a bunch of derived classes". Maybe I didn't explain much, but my question was pretty clear.. I appreciate people trying to correct me, but what I appreciate most is getting help with the problem at hand.
cloudhead
But that bit of information was important. To me (and others), it looked like you were using inheritance where composition would be better suited to the task.
Ed Swangren
@ed I'd calm down and answer the question. He might be doing something that violates your OO principles (and perhaps mine as well), but we don't have enough information to determine that so I'd stick to just showing him Ruby's power in this respect and leave it be.
Yehuda Katz
@Yehuda Katz, thanks I guess, but I am just responding to the comments. I'm not angry...
Ed Swangren
+2  A: 

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.

wuub
respond_to? isn't always cleaner, it isn't because it responds to one method that it'll respond to all other calls you send to it afterwards. So checking once if the type is correct is better in this case.
cloudhead
+1  A: 

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.

Chuck
yea, I didn't know included modules would behave like superclasses in respect to is_a? - that pretty much makes it a non-issue
cloudhead
+3  A: 

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 = String.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
Yehuda Katz
oh wow, I didn't know is_a? responded `true` for mixins, and it even works if the included module includes another module! I'll probably use this then, instead of forwardable. thanks!
cloudhead
typo: should be `world = Stringy.new("World")`
rampion