Why you should probably avoid mixins
rubyAs you might have heard inheritance is evil. I won’t argue, but indeed often inheritance is used improperly. The only legitimate usage of inheritance is creating more specific entity, not sharing the code. In this blog post I’ll assume that inheritance is evil when used for code sharing.
Mixins are another syntax for inheritance
TL;TD And they are used for code sharing, thus mixins are evil. Indeed, in Ruby included module becomes an implicit superclass with all perks of inheritance. And this can be abused.
1) super and method overriding
module One
def func
puts "in module"
end
end
class Two
include One
def func
puts "in class"
super
end
end
# take a look at object hierarchy
Two.ancestors
[
[0] Two < Object,
[1] One, # implicit superclass
[2] Object < BasicObject,
[3] PP::ObjectMixin,
[4] Kernel,
[5] BasicObject
]
# call a function
Two.new.func
in class
in module # <- yes, you can call `super`
You can also include many modules with the same methods:
module One
def func
puts "in one"
end
end
module Two
def func
puts "in two"
end
end
class Three
include One
include Two
def func
puts "in class"
super
end
end
Three.ancestors
[
[0] Three < Object,
[1] Two,
[2] One,
[3] Object < BasicObject,
[4] PP::ObjectMixin,
[5] Kernel,
[6] BasicObject
]
Three.new.func #=> what do you expect here?
The last example can be an interview question. It requires knowledge of how modules and inheritance actually work.
2) they have access to instance variables of a class
Being just a superclass, included module can access private instance variables of a child:
module One
def func
puts @var
end
end
class Two
include One
def initialize
@var = 'hello'
end
end
Two.new.func #=> hello
Same is true for private methods.
Evil begins
Now a developer can abuse mixins and make his code an unreadable mess. With good intentions, of course. For example, why not include 10 modules in 1 class? Now I have a “small” class and everything else spread across 10 entangled modules. Did I mention that you can call methods of another module from withing a module? Multiply this possibility by method overriding and instance variables and here comes a mess.
Is this really bad? Yes! You should keep you classes and method small, but when you split a big class into modules you make things worse. Now you have not only a big class, but a big class scattered across multiple files making it harder to read.
Conclusion
Mixins are just cheaty syntax for inheritance intended for code sharing. Avoid them if you can. Prefer composition instead. This is true for any kind of inheritance, and especially for mixins.
In my opinion, the only legit case for mixins is when your module has no dependency on a class where it’s included. But even if it’s true for now doesn’t mean other developers cannot add dependency later. There is no barrier.