Me acaba de leer acerca de este tema en el Rubyist Grounded-Bueno (gran libro, por cierto). El autor explica mejor que yo, así que lo citaré:
Ninguna regla o fórmula única siempre da como resultado el diseño correcto. Pero es útil tener en cuenta un par de consideraciones al tomar decisiones de clase versus módulo:
Los módulos no tienen instancias. De ello se deduce que las entidades o cosas generalmente se modelan mejor en clases, y las características o propiedades de entidades o cosas se encapsulan mejor en módulos. En consecuencia, como se señaló en la sección 4.1.1, los nombres de clase tienden a ser sustantivos, mientras que los nombres de módulos son a menudo adjetivos (Stack versus Stacklike).
Una clase solo puede tener una superclase, pero puede mezclar tantos módulos como desee. Si está utilizando la herencia, dé prioridad a la creación de una relación sensata de superclase / subclase. No use la única relación de superclase de una clase para dotar a la clase de lo que podría resultar ser uno de varios conjuntos de características.
Resumiendo estas reglas en un ejemplo, esto es lo que no debe hacer:
module Vehicle
...
class SelfPropelling
...
class Truck < SelfPropelling
include Vehicle
...
Más bien, debes hacer esto:
module SelfPropelling
...
class Vehicle
include SelfPropelling
...
class Truck < Vehicle
...
La segunda versión modela las entidades y propiedades de manera mucho más clara. El camión desciende del vehículo (lo cual tiene sentido), mientras que la autopropulsión es una característica de los vehículos (al menos, todos aquellos que nos importan en este modelo del mundo), una característica que se transmite a los camiones en virtud de que Truck es un descendiente, o forma especializada, de Vehículo.
Truck
IS AVehicle
: no hay ningunoTruck
que no sea aVehicle
. Sin embargo, llamaría al módulo quizásSelfPropelable
(:?) HmmSelfPropeled
suena bien, pero es casi lo mismo: D. De todos modos, no lo incluiríaVehicle
sino enTruck
, ya que HAY vehículos que NO SONSelfPropeled
. También es una buena indicación preguntar: ¿hay otras cosas, NO vehículos que SONSelfPropeled
? - Bueno, tal vez, pero sería más difícil de encontrar. PorVehicle
lo tanto, podría heredar de la clase Autopropulsión (como clase no encajaría comoSelfPropeled
, ya que es más un papel)Creo que los mixins son una gran idea, pero hay otro problema aquí que nadie ha mencionado: las colisiones de espacios de nombres. Considerar:
¿Cuál gana? En Ruby, resulta ser el último
module B
, porque lo incluiste despuésmodule A
. Ahora, es fácil de evitar este problema: asegurarse de que todosmodule A
ymodule B
's constantes y métodos están en espacios de nombres improbables. El problema es que el compilador no te advierte en absoluto cuando ocurren colisiones.Sostengo que este comportamiento no se adapta a grandes equipos de programadores; no debe suponer que la persona que implementa
class C
conoce todos los nombres en el alcance. Ruby incluso te permitirá anular una constante o un método de un tipo diferente . No estoy seguro de que alguna vez pueda considerarse un comportamiento correcto.fuente
C#sayhi
resultadosB::HELLO
no se deben a que Ruby mezcla las constantes, sino porque ruby resuelve las constantes de más cerca a más distantes, por lo que siempre se resuelve laHELLO
referencia en . Esto es válido incluso si la clase C definió que es propia también.B
B::HELLO
C::HELLO
Mi opinión: los módulos son para compartir comportamientos, mientras que las clases son para modelar relaciones entre objetos. Técnicamente, podría hacer que todo sea una instancia de Object y mezclar los módulos que desee para obtener el conjunto deseado de comportamientos, pero eso sería un diseño pobre, fortuito y bastante ilegible.
fuente
La respuesta a su pregunta es en gran medida contextual. Al destilar la observación de pubb, la elección está impulsada principalmente por el dominio en consideración.
Y sí, ActiveRecord debería haber sido incluido en lugar de extendido por una subclase. ¡Otro ORM - datamapper - logra precisamente eso!
fuente
Me gusta mucho la respuesta de Andy Gaskell: solo quería agregar que sí, ActiveRecord no debería usar la herencia, sino más bien incluir un módulo para agregar el comportamiento (principalmente persistencia) a un modelo / clase. ActiveRecord simplemente está usando el paradigma incorrecto.
Por la misma razón, me gusta mucho MongoId sobre MongoMapper, porque le da al desarrollador la oportunidad de usar la herencia como una forma de modelar algo significativo en el dominio del problema.
Es triste que casi nadie en la comunidad de Rails esté usando la "herencia de Ruby" de la forma en que se supone que debe usarse: para definir jerarquías de clase, no solo para agregar comportamiento.
fuente
La mejor forma en que entiendo los mixins es como clases virtuales. Los mixins son "clases virtuales" que se han inyectado en la cadena de ancestros de una clase o módulo.
Cuando usamos "include" y le pasamos un módulo, agrega el módulo a la cadena de antecesores justo antes de la clase de la que heredamos:
Cada objeto en Ruby también tiene una clase singleton. Los métodos agregados a esta clase singleton se pueden invocar directamente en el objeto, por lo que actúan como métodos de "clase". Cuando usamos "extender" en un objeto y pasamos el objeto a un módulo, estamos agregando los métodos del módulo a la clase singleton del objeto:
Podemos acceder a la clase singleton con el método singleton_class:
Ruby proporciona algunos ganchos para los módulos cuando se mezclan en clases / módulos.
included
es un método de enlace proporcionado por Ruby que se llama cada vez que incluye un módulo en algún módulo o clase. Al igual que se incluye, hay unextended
gancho asociado para extender. Se llamará cuando un módulo sea extendido por otro módulo o clase.Esto crea un patrón interesante que los desarrolladores podrían usar:
Como puede ver, este módulo único está agregando métodos de instancia, métodos de "clase" y actuando directamente sobre la clase de destino (en este caso, llamando a a_class_method ()).
ActiveSupport :: Preocupación encapsula este patrón. Aquí está el mismo módulo reescrito para usar ActiveSupport :: Preocupación:
fuente
En este momento, estoy pensando en el
template
patrón de diseño. Simplemente no se sentiría bien con un módulo.fuente