6

Here is an example of multi level inheritance in ruby, here we have 3 classes A, B and C. B inherits from A and C inherits from B, so at the end class C has all methods of A, B and C.

class A
  def hello_by_a
    puts "A says hello"
  end
end

class B < A
  def hello_by_b
    puts "B says hello"
  end
end

class C < B
  def hello_by_c
    puts "C says hello"
  end
end

c = C.new
c.hello_by_a #=> A says hello
c.hello_by_b #=> B says hello
c.hello_by_c #=> C says hello

p c.methods-Object.methods #=> [:hello_by_c, :hello_by_b, :hello_by_a]

And here is the same thing as mixins, here instead of classes A and B, we have modules A and B which gets included to class C. Now class C has all 3 methods

module A
  def hello_by_a
    puts "A says hello"
  end
end

module B
  def hello_by_b
    puts "B says hello"
  end
end

class C
  include A
  include B

  def hello_by_c
    puts "C says hello"
  end
end

c = C.new

c.hello_by_a #=> A says hello
c.hello_by_b #=> B says hello
c.hello_by_c #=> C says hello

p c.methods-Object.methods #=> [:hello_by_c, :hello_by_b, :hello_by_a]

At the end if we do it in both ways class C will have all the methods of class A and B or module A and B. So why is it better to use modules instead of multilevel inheritance with classes?

I know we should use mixins but don't really know why we shouldn't use multilevel inheritance like above. What are the disadvantages and advantages. if any?

2
  • 2
    Good question, and well asked. Commented Sep 20, 2019 at 17:40
  • 2
    I once heard the rule of thumb: When A inherits from B then that basically means A is a B. Whereas when A include B then that only means that A acts as a B. Commented Sep 20, 2019 at 18:17

1 Answer 1

7

Two main reasons:

You can only inherit from one class, but you can mix in as many mixins as you want. This means that inheritance is extremely "expensive" in the sense that if you are forced to use inheritance, you are forced to "use up" 100% of your "inheritance resources".

You can compose mixins any way you want. In your example, I can only get the methods of A, the methods of AB, and the methods of ABC. I can, for example, not get only the methods of B. With mixins, I can compose them in any combination: only A, only B, only C, AB, AC, BC, and ABC. I can also compose them in any order I want: I can have the methods of C override the methods of B or I can have the methods of B override the methods of C.

I can easily imagine an object that is both Enumerable and Comparable. (For example, Strings are Comparable and before Ruby 1.9, they also used to be Enumerable.) In your proposed world, this would only be possible if either Enumerable inherits from Comparable or Comparable inherits from Enumerable. However, this will not work: Numerics are Comparable but not Enumerable, Arrays are Enumerable but not Comparable.

There is also a more philosophical / semantic reason: enumerability and comparability are completely orthogonal concepts, why would you tie them together so closely? Inheritance is one of the closest forms of coupling we have, tying two concepts to closely together that don't actually have anything to do with each other, is just wrong.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.