Los Módulos como Mixins

  1. Modules have another, wonderful use.

    At a stroke, they pretty much eliminate the need for inheritance, providing a facility called a mixin.

  2. What happens if I define instance methods within a module?

    Good question

  3. A module can’t have instances, because a module isn’t a class.

  4. However, you can include a module within a class definition.

    When this happens, all the module’s instance methods are suddenly available as methods in the class as well.

  5. They get mixed in.

  6. In fact, mixed-in modules effectively behave as superclasses.

Si un módulo define métodos de instancia en vez de métodos de clase, esos métodos de instancia pueden ser combinados/mezclados (mixin) en otras clases.

Para mezclar un módulo en una clase llamamos al método include:

class Point
  include Enumerable

  1. Thanks to the magic of mixins and module Enumerable. All you have to do is write an iterator called each, which returns the elements of your collection in turn. Mix in Enumerable, and suddenly your class supports things such as map, include?, and find_all?.
  2. If the objects in your collection implement meaningful ordering semantics using the <=> method, you’ll also get methods such as min, max, and sort.

include es un método privado de instancia de Module:

ruby-1.9.2-head :004 > {|x| x =~ /inc/ }
 => [:included, :include]
Es legal incluir un módulo en una clase o en un módulo. Al hacerlo los métodos de instancia del módulo incluído se convierten en métodos de instancia del incluyente.

  module D
    def initialize(name)
     @name =name
    def to_s

  module Debug
  include D
    def who_am_i?
      "#{} (\##{self.__id__}): #{self.to_s}"

  class Phonograph
    include Debug
    # ...

  class EightTrack
    include Debug
    # ...

  ph ="West End Blues")      # "Phonograph (#70270868282220): West End Blues"º)
  et ="Surrealistic Pillow") # "EightTrack (#70270868320420): Surrealistic Pillow")

We’ll make a couple of points about the include statement before we go on.

  1. First, it has nothing to do with files.
  2. C programmers use a preprocessor directive called #include to insert the contents of one file into another during compilation. The Ruby include statement simply makes a reference to a module.
  3. If that module is in a separate file, you must use require (or its less commonly used cousin, load) to drag that file in before using include.
  4. Second, a Ruby include does not simply copy the module’s instance methods into the class. Instead, it makes a reference from the class to the included module.
  5. If multiple classes include that module, they’ll all point to the same thing.
  6. If you change the definition of a method within a module, even while your program is running, all classes that include that module will exhibit the new behavior.

Dave Thomas

Variables de Instancia en Mixins

  1. Remember how instance variables work in Ruby: the first mention of an @-prefixed variable creates the instance variable in the current object, self.

  2. For a mixin, this means that the module you mix into your client class may create instance variables in the client object and may use attr_reader and friends to define accessors for these instance variables.
  3. However, this behavior exposes us to a risk. A mixin’s instance variables can clash with those of the host class or with those of other mixins.

The example that follows shows a class that uses our Observer module but that unluckily also uses an instance variable called @observer_list.

  module Observable
    def observers
      @observer_list ||= []

    def add_observer(obj)
      observers << obj

    def notify_observers
      observers.each {|o| o.update }

require 'code/observer_impl'
class TelescopeScheduler

  # other classes can register to get notifications
  # when the schedule changes
  include Observable   

  def initialize
    @observer_list = []  # folks with telescope time

  def add_viewer(viewer)
    @observer_list << viewer

  # ...
At runtime, this program will go wrong in some hard-to-diagnose ways.

  1. For the most part, mixin modules don’t use instance variables directly—they use accessors to retrieve data from the client object
  2. But if you need to create a mixin that has to have its own state, ensure that the instance variables have unique names to distinguish them from any other mixins in the system (perhaps by using the module’s name as part of the variable name).
  3. Alternatively, the module could use a module-level hash, indexed by the current object ID, to store instance-specific data without using Ruby instance variables. A downside of this approach is that the data associated with a particular object will not get automatically deleted if the object is deleted.

Dave Thomas

[~/local/src/ruby/rubytesting/programmingRuby/instance_variables_in_mixins]$ cat private_state_in_mixins.rb 
require 'pp'

module Test
  State = {}
  def state=(value)
    State[__id__] = value
  def state

  def delete_state

class Client
  include Test

c1 =
c2 =
c1.state = 'cat'
c2.state = 'dog'

pp Test::State.inspect # {70259441214220=>"cat", 70259441214140=>"dog"}

pp c1.state

pp Test::State.inspect # {70259441214140=>"dog"}

pp c2.state

[~/local/src/ruby/rubytesting/programmingRuby/instance_variables_in_mixins]$ ruby private_state_in_mixins.rb 
"{70278990371120=>\"cat\", 70278990371100=>\"dog\"}"


One of the other questions folks ask about mixins is, how is method lookup handled? In particular, what happens if methods with the same name are defined in a class, in that class’s parent class, and in a mixin included into the class?

  1. The answer is that Ruby looks first in the immediate class of an object,
  2. then in the mixins included into that class,
  3. and then in superclasses and their mixins.
  4. If a class has multiple modules mixed in, the last one included is searched first.

Dave Thomas

¿Que ocurre si se incluyen dos módulos en los que existen métodos con el mismo nombre?

[~/TheRubyProgrammingLanguage/Chapter7ClassesAndModules/modules]$ cat module_collision.rb 
module M1
  def foo
    puts "M1"

module M2
  def foo
    puts "M2"

Cuando ambos módulos son incluídos en la clase C, la última definición de foo es la que domina:

[~/TheRubyProgrammingLanguage/Chapter7ClassesAndModules/modules]$ cat class_require_module_collision.rb 
require 'module_collision'
class C
  include M1
  include M2
end # 'M2'
puts C.ancestors.inspect # => [C, M2, M1, Object, Kernel, BasicObject]

Ahora bien, si en la clase existe un método foo ese será encontrado primero:

[~/TheRubyProgrammingLanguage/Chapter7ClassesAndModules/modules]$ cat -n module_class_collision.rb 
 1  module M
 2    def foo
 3      puts "M"
 4    end
 5  end
 7  class C
 8    def foo
 9      puts "C"
10    end
11    include M
12  end
13                # => "C"
14  puts C.ancestors.inspect # => [C, M, Object, Kernel, BasicObject]

Composición frente a Herencia/Composition versus Inheritance

Inheritance and mixins both allow you to write code in one place and effectively inject that code into multiple classes.

So, when do you use each?

  1. First, let’s look at subclassing. Classes in Ruby are related to the idea of types.

  2. When we create our own classes, you can think of it as adding new types to the language.

  3. And when we subclass either a built-in class or our own class, we’re creating a subtype.

  4. Now, a lot of research has been done on type theories. One of the more famous results is the Liskov Substitution Principle. Formally, this states:

    Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T

  5. What this means is that you should be able to substitute an object of a child class wherever you use an object of the parent class—the child should honor the parent’s contract.

    We’re used to saying this in English: a car is a vehicle, a cat is an animal, and so on.

    This means that a cat should, at the very least, be capable of doing everything we say that an animal can do.

  6. So, when you’re looking for subclassing relationships while designing your application, be on the lookout for these is-a relationships.

  7.’s the bad news. In the real world, there really aren’t that many true is a relationships. Instead, it’s far more common to have has a or uses a relationships between things.

Dave Thomas

  1. Think of containment as a has a relationship. A car has an engine, a person has a name, etc.
  2. Think of inheritance as an is a relationship. A car is a vehicle, a person is a mammal, etc.


Aunque Module.include es la forma normal de mezclar un módulo también es posible mezclar con Object.extend. Este método hace que los métodos de instancia del módulo especificado se incorporen como métodos singleton del objeto receptor. Si el objeto receptor es una clase, esto es, es un objeto de la clase Class, entonces los métodos son métodos de clase de dicha clase receptora.

[~/srcLPP/Chapter7ClassesAndModules/modules]$ cat module_mixin_with_extend.rb 
module Chuchu
  def tutu
    "method tutu. self: #{self.inspect}"

class ChanChan

p = "hello "
q = "world"
p.extend Chuchu

puts p.tutu

ChanChan.extend Chuchu
puts ChanChan.tutu

puts q.tutu # exception
[~/srcLPP/Chapter7ClassesAndModules/modules]$ ruby module_mixin_with_extend.rb 
method tutu. self: "hello "
method tutu. self: ChanChan
module_mixin_with_extend.rb:19:in `<main>': undefined method `tutu' for "world":String (NoMethodError)

Casiano Rodriguez León 2015-01-07