ruby inheritance vs mixins
I just read about this topic in The Well-Grounded Rubyist (great book, by the way). The author does a better job of explaining than I would so I'll quote him:
No single rule or formula always results in the right design. But it’s useful to keep a
couple of considerations in mind when you’re making class-versus-module decisions:
Modules don’t have instances. It follows that entities or things are generally best
modeled in classes, and characteristics or properties of entities or things are
best encapsulated in modules. Correspondingly, as noted in section 4.1.1, class
names tend to be nouns, whereas module names are often adjectives (Stack
versus Stacklike).A class can have only one superclass, but it can mix in as many modules as it wants. If
you’re using inheritance, give priority to creating a sensible superclass/subclass
relationship. Don’t use up a class’s one and only superclass relationship to
endow the class with what might turn out to be just one of several sets of characteristics.
Summing up these rules in one example, here is what you should not do:
module Vehicle
...
class SelfPropelling
...
class Truck < SelfPropelling
include Vehicle
...
Rather, you should do this:
module SelfPropelling
...
class Vehicle
include SelfPropelling
...
class Truck < Vehicle
...
The second version models the entities and properties much more neatly. Truck
descends from Vehicle (which makes sense), whereas SelfPropelling is a characteristic of vehicles (at least, all those we care about in this model of the world)—a characteristic that is passed on to trucks by virtue of Truck being a descendant, or specialized
form, of Vehicle.
Why does DataMapper use mixins vs inheritance?
I think the idea is that ActiveRecord considers the database backed aspect to be the key feature of a model class so it inherits that behavior. DataMapper looks like it considers being database backed to just be an aspect of a class that can be added to a class.
That's my guess. Yehuda Katz could tell you definitively.
Using multi-level inheritance instead of ruby mixins
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 A
∪ B
, and the methods of A
∪ B
∪ C
. 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
, A
∪ B
, A
∪ C
, B
∪ C
, and A
∪ B
∪ C
. 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, String
s 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: Numeric
s are Comparable
but not Enumerable
, Array
s 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.
Ruby: mixins, inheritance and constants name resolution
Due to the way constants are resolved based on definition scope and not in inherited scopes you'll need to bridge that with a method call:
class ABase
def action
# Within this scope only constants defined in `ABase` are resolved.
# As ABase::C and ::C (root-level) don't exist, C can't be resolved.
# However, a method `c` defined in a subclass will be.
c.new.foo
end
end
module M1
class A < ABase
def c
C
end
end
class C
def foo
puts "M1::C foo"
end
end
end
module M2
class A < ABase
# This resolves C as either M2::A::C, M2::ABase::C, M2::C or ::C,
# whichever it finds first.
def c
C
end
end
class C
def foo
puts "M2::C foo"
end
end
end
Then you get the expected results:
M1::A.new.action
# => M1::C foo
M2::A.new.action
# => M2::C foo
When do we use ruby module vs using class composition?
When the relationship between Helper
and the MyStuff
class is one of ownership, use composition. This is known as a "has-a" relationship. For example, let's say you have Person
class and a Car
class. You would use composition because a person has a car:
class Person
def initialize
@car = Car.new
end
end
class Car
def accelerate
# implementation
end
end
When Helper
"acts like" MyStuff
, use a module mixin. Helper
, in this case, takes on the role of MyStuff
. This is a bit different than a "is-a" relationship, which would imply that you should use traditional inheritance. For example, let's say we have a Person
class and a Sleeper
module. A person takes on the role of a sleeper sometimes, but so do other objects--instances of Dog
, Frog
, or maybe even Computer
. Each of those other classes represent something that can go to sleep.
module Sleeper
def go_to_sleep
# implementation
end
end
class Person
include Sleeper
end
class Computer
include Sleeper
end
Sandi Metz's Practical Object-Oriented Design in Ruby is an excellent resource for these topics.
Are modules == mixins in ruby?
Mixins are a language concept that allows to inject some code into a class.
This is implemented in Ruby by the keyword include
that takes a Module
as a parameter.
So yes, in Ruby, mixins are implemented with modules. But modules have other uses than mixins.
For example, modules can also be used for namespacing your classes or encapsulating utility functions to keep from polluting the global namespace.
Use mixin method over inherited one
If you can modify the source code of Node
, then let it prepend Enumerable
instead of include Enumerable
.
If you can't, then you can grab the instance method select
from Enumerable
and bind
it to the instance of Node
, then call
it.
node = Node.new(...)
Enumerable.instance_method(:select).bind(node).call
Related Topics
Get the Name of the Currently Executing Method
How to Create a Deep Copy of an Object in Ruby
Ruby: Kind_Of? Vs. Instance_Of? Vs. Is_A
Rails Keeps Telling Me That It's Not Currently Installed
Best Way to Convert Strings to Symbols in Hash
How to Convert a Ruby Hash Object to Json
Valid Email Address Regular Expression
Is the Ruby Operator ||= Intelligent
How to Get Source Code of a Method Dynamically and Also Which File Is This Method Locate In
Converting a Nested Hash into a Flat Hash
How to Specify a Local Gem in My Gemfile