¿Qué es un mixin y por qué son útiles?

955

En " Programación de Python ", Mark Lutz menciona "mixins". Soy de un fondo C / C ++ / C # y no he escuchado el término antes. ¿Qué es un mixin?

Leyendo entre las líneas de este ejemplo (al que me he vinculado porque es bastante largo), supongo que es un caso de usar herencia múltiple para extender una clase en lugar de una subclase 'adecuada'. ¿Es esto correcto?

¿Por qué querría hacer eso en lugar de poner la nueva funcionalidad en una subclase? Para el caso, ¿por qué sería mejor un enfoque de herencia mixin / múltiple que usar composición?

¿Qué separa una mezcla de herencia múltiple? ¿Es solo una cuestión de semántica?

TarkaDaal
fuente

Respuestas:

710

Un mixin es un tipo especial de herencia múltiple. Hay dos situaciones principales donde se usan mixins:

  1. Desea proporcionar muchas características opcionales para una clase.
  2. Desea utilizar una función particular en muchas clases diferentes.

Para un ejemplo del número uno, considere el sistema de solicitud y respuesta de werkzeug . Puedo hacer un objeto de solicitud antiguo simple diciendo:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Si quiero agregar aceptar el soporte de encabezado, haría que

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Si quisiera hacer un objeto de solicitud que admita aceptar encabezados, etags, autenticación y soporte de agente de usuario, podría hacer esto:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
    pass

La diferencia es sutil, pero en los ejemplos anteriores, las clases mixin no fueron creadas por sí mismas. En la herencia múltiple más tradicional, AuthenticationMixin(por ejemplo) probablemente sería algo más parecido Authenticator. Es decir, la clase probablemente estaría diseñada para sostenerse por sí misma.

Jason Baker
fuente
123
Una tercera situación es: desea proporcionar muchas características (no opcionales) para una clase, pero desea las características en clases separadas (y en módulos separados) para que cada módulo sea sobre una característica (comportamiento). para reutilización, pero para compartimentación.
bootchk
6060
Probablemente no sea un problema en este ejemplo, pero generalmente desea colocar la clase base principal como el último elemento dentro del paréntesis para crear la cadena de herencia: Solicitud ==> Mixin ==> ... ==> BaseRequest. Ver aquí: ianlewis.org/en/mixins-and-python
hillel
10
@hillel buen punto, pero tenga en cuenta que Python llamará a los métodos de superclases de izquierda a derecha (cuando necesite anular el constructor, por ejemplo).
Eliseu Monar dos Santos
99
Esto suena mucho como el patrón de diseño Decorador.
D-Jones
44
Una cuarta situación es: ya hay una familia existente de Parentclase y Child1, Child2, ChildNsubclases dentro de una biblioteca tercera parte, y se desea un comportamiento personalizado para toda la familia. Idealmente, le gustaría agregar ese comportamiento Parenty esperar que el desarrollador de la biblioteca de terceros tome su solicitud de extracción. De lo contrario, tendrá que implementar el suyo propio class NewBehaviorMixiny luego definir un conjunto completo de clases de contenedor como class NewParent(NewBehaviorMixin, Parent): passy class NewChildN(NewBehaviorMixin, ChildN): pass, etc. (PD: ¿Conoce una mejor manera?)
RayLuo
240

Primero, debe tener en cuenta que los mixins solo existen en lenguajes de herencia múltiple. No puedes hacer una mezcla en Java o C #.

Básicamente, un mixin es un tipo de base independiente que proporciona funcionalidad limitada y resonancia polimórfica para una clase infantil. Si está pensando en C #, piense en una interfaz que no tiene que implementar realmente porque ya está implementada; simplemente heredas de él y te beneficias de su funcionalidad.

Las mixinas son típicamente de alcance estrecho y no están destinadas a extenderse.

[editar - en cuanto a por qué:]

Supongo que debería abordar por qué, ya que lo preguntaste. El gran beneficio es que no tiene que hacerlo usted mismo una y otra vez. En C #, el lugar más grande donde un mixin podría beneficiarse podría ser el patrón de eliminación . Cada vez que implementa IDisposable, casi siempre desea seguir el mismo patrón, pero termina escribiendo y reescribiendo el mismo código básico con pequeñas variaciones. Si hubiera una mezcla de eliminación extensible, podría ahorrarse una gran cantidad de tipeo adicional.

[editar 2 - para responder a sus otras preguntas]

¿Qué separa una mezcla de herencia múltiple? ¿Es solo una cuestión de semántica?

Si. La diferencia entre un mixin y una herencia múltiple estándar es solo una cuestión de semántica; una clase que tiene herencia múltiple podría utilizar un mixin como parte de esa herencia múltiple.

El objetivo de un mixin es crear un tipo que se pueda "mezclar" con cualquier otro tipo a través de la herencia sin afectar el tipo de herencia y al mismo tiempo ofrecer alguna funcionalidad beneficiosa para ese tipo.

Nuevamente, piense en una interfaz que ya está implementada.

Personalmente no uso mixins, ya que me desarrollo principalmente en un lenguaje que no los admite, por lo que me está costando mucho encontrar un ejemplo decente que solo proporcione ese "¡ajá!" momento para ti Pero lo intentaré de nuevo. Voy a usar un ejemplo que es artificial: la mayoría de los idiomas ya proporcionan la función de una forma u otra, pero eso, con suerte, explicará cómo se supone que se deben crear y usar mixins. Aquí va:

Supongamos que tiene un tipo que desea poder serializar hacia y desde XML. Desea que el tipo proporcione un método "ToXML" que devuelve una cadena que contiene un fragmento XML con los valores de datos del tipo y un "FromXML" que permite que el tipo reconstruya sus valores de datos a partir de un fragmento XML en una cadena. Una vez más, este es un ejemplo artificial, por lo que quizás use una secuencia de archivos o una clase de Escritor XML de la biblioteca de tiempo de ejecución de su idioma ... lo que sea. El punto es que desea serializar su objeto a XML y recuperar un nuevo objeto de XML.

El otro punto importante en este ejemplo es que desea hacer esto de manera genérica. No desea tener que implementar un método "ToXML" y "FromXML" para cada tipo que desea serializar, desea algunos medios genéricos para asegurarse de que su tipo haga esto y simplemente funcione. Quieres reutilizar el código.

Si su idioma lo admite, puede crear el mixin serializable Xml para que haga su trabajo por usted. Este tipo implementaría los métodos ToXML y FromXML. Con un mecanismo que no es importante para el ejemplo, sería capaz de recopilar todos los datos necesarios de cualquier tipo con el que se mezcle para construir el fragmento XML devuelto por ToXML y sería igualmente capaz de restaurar esos datos cuando FromXML sea llamado.

Y eso es. Para usarlo, debe tener cualquier tipo que necesite ser serializado a XML heredado de XmlSerializable. Siempre que necesite serializar o deserializar ese tipo, simplemente llame a ToXML o FromXML. De hecho, dado que XmlSerializable es un tipo completo y polimórfico, posiblemente podría construir un serializador de documentos que no sepa nada sobre su tipo original, aceptando solo, por ejemplo, una matriz de tipos XmlSerializable.

Ahora imagine usar este escenario para otras cosas, como crear un mixin que garantice que cada clase que lo mezcla en el registro registre todas las llamadas a métodos, o un mixin que proporcione transaccionalidad al tipo que lo mezcla. La lista puede seguir y seguir.

Si solo piensas en un mixin como un tipo base pequeño diseñado para agregar una pequeña cantidad de funcionalidad a un tipo sin afectar ese tipo, entonces eres dorado.

Ojalá. :)

Randolpho
fuente
25
Oye, ¿te gusta esa frase "resonancia polimórfica"? Lo inventé yo mismo. Yo creo que. Tal vez lo escuché en física en alguna parte ...
Randolpho
50
No estoy de acuerdo con tu primera oración. Ruby es un lenguaje de herencia única y los mixins son la forma de agregar métodos a una clase dada sin heredar de otra clase.
Keltia
23
@Keltia: Creo que los mixin son, por definición, herencia múltiple. En el caso de Ruby, son un parche de mono (o algo más) que no es una mezcla adecuada. La gente de Ruby puede llamarlo un mixin, pero es un tipo diferente de cosas.
S.Lott
10
En realidad, un verdadero mixin no puede usar herencia múltiple. Un mixin incluye métodos, atributos, etc. de una clase en otra sin heredarlo. Esto tiende a dar los beneficios de la reutilización de código que parece con polimorfismo, pero deja de lado los problemas que determinan la paternidad (el diamante de la muerte, etc.) Los lenguajes compatibles con Mixin también tienden a permitir la inclusión parcial de la clase mixin (las cosas comienzan a sonar un poco como aspectos ahora).
Trevor
8
Para el registro, Java ahora admite mixins con métodos predeterminados.
shmosel
170

Esta respuesta tiene como objetivo explicar los mixins con ejemplos que son:

  • autocontenido : breve, sin necesidad de conocer ninguna biblioteca para comprender el ejemplo.

  • en Python , no en otros idiomas.

    Es comprensible que haya ejemplos de otros idiomas, como Ruby, ya que el término es mucho más común en esos idiomas, pero este es un hilo de Python .

También considerará la controvertida pregunta:

¿Es necesaria la herencia múltiple o no para caracterizar un mixin?

Definiciones

Todavía tengo que ver una cita de una fuente "autorizada" que diga claramente qué es un mixin en Python.

He visto 2 posibles definiciones de un mixin (si se consideran diferentes de otros conceptos similares, como las clases base abstractas), y las personas no están totalmente de acuerdo sobre cuál es la correcta.

El consenso puede variar entre diferentes idiomas.

Definición 1: sin herencia múltiple

Un mixin es una clase tal que algún método de la clase usa un método que no está definido en la clase.

Por lo tanto, la clase no debe ser instanciada, sino que sirve como una clase base. De lo contrario, la instancia tendría métodos que no se pueden invocar sin generar una excepción.

Una restricción que agregan algunas fuentes es que la clase puede no contener datos, solo métodos, pero no veo por qué esto es necesario. Sin embargo, en la práctica, muchos mixins útiles no tienen datos, y las clases base sin datos son más fáciles de usar.

Un ejemplo clásico es la implementación de todos los operadores de comparación de solo <=y ==:

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

Este ejemplo en particular podría haberse logrado a través del functools.total_ordering()decorador, pero el juego aquí fue reinventar la rueda:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

Definición 2: herencia múltiple

Un mixin es un patrón de diseño en el que algún método de una clase base usa un método que no define, y ese método está destinado a ser implementado por otra clase base , no por el derivado como en la Definición 1.

El término clase mixin se refiere a las clases base que están destinadas a usarse en ese patrón de diseño (¿TODOS los que usan el método o los que lo implementan?)

No es fácil decidir si una clase dada es una combinación o no: el método podría implementarse solo en la clase derivada, en cuyo caso volvemos a la Definición 1. Debe considerar las intenciones del autor.

Este patrón es interesante porque es posible recombinar funcionalidades con diferentes opciones de clases base:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

Ocurrencias autorizadas de Python

En la documentación oficial de collections.abc, la documentación utiliza explícitamente el término Métodos Mixin .

Establece que si una clase:

  • implementos __next__
  • hereda de una sola clase Iterator

entonces la clase obtiene un __iter__ método mixin gratis.

Por lo tanto, al menos en este punto de la documentación, mixin no requiere herencia múltiple , y es coherente con la Definición 1.

La documentación podría ser, por supuesto, contradictoria en diferentes puntos, y otras bibliotecas importantes de Python podrían estar utilizando la otra definición en su documentación.

Esta página también usa el término Set mixin, que sugiere claramente que a las clases les gustan Sety Iteratorpueden llamarse clases Mixin.

En otros idiomas

  • Ruby: Claramente no requiere herencia múltiple para mixin, como se menciona en los principales libros de referencia como Programming Ruby y The Ruby programación Language

  • C ++: un método que no se implementa es un método virtual puro.

    La definición 1 coincide con la definición de una clase abstracta (una clase que tiene un método virtual puro). Esa clase no puede ser instanciada.

    La definición 2 es posible con herencia virtual: herencia múltiple de dos clases derivadas

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
37

Pienso en ellos como una forma disciplinada de usar la herencia múltiple, porque en última instancia, un mixin es solo otra clase de python que (podría) seguir las convenciones sobre las clases que se llaman mixins.

Mi comprensión de las convenciones que rigen algo que llamarías Mixin es que un Mixin:

  • agrega métodos pero no variables de instancia (las constantes de clase están bien)
  • solo hereda de object(en Python)

De esta forma, limita la complejidad potencial de la herencia múltiple y hace que sea razonablemente fácil rastrear el flujo de su programa al limitar dónde debe mirar (en comparación con la herencia múltiple completa). Son similares a los módulos ruby .

Si quiero agregar variables de instancia (con más flexibilidad que la permitida por una sola herencia), entonces tiendo a elegir la composición.

Dicho esto, he visto clases llamadas XYZMixin que tienen variables de instancia.

Hamish Downer
fuente
30

Mixins es un concepto en Programación en el que la clase proporciona funcionalidades pero no está destinado a ser utilizado para la creación de instancias. El propósito principal de Mixins es proporcionar funcionalidades que son independientes y sería mejor si las mixins en sí no tienen herencia con otras mixins y también evitan el estado. En lenguajes como Ruby, hay algún soporte directo de lenguaje, pero para Python, no lo hay. Sin embargo, podría usar la herencia de varias clases para ejecutar la funcionalidad proporcionada en Python.

Vi este video http://www.youtube.com/watch?v=v_uKI2NOLEM para comprender los conceptos básicos de los mixins. Es bastante útil para un principiante comprender los conceptos básicos de los mixins y cómo funcionan y los problemas que puede enfrentar al implementarlos.

Wikipedia sigue siendo la mejor: http://en.wikipedia.org/wiki/Mixin

Lakesh
fuente
29

¿Qué separa una mezcla de herencia múltiple? ¿Es solo una cuestión de semántica?

Un mixin es una forma limitada de herencia múltiple. En algunos idiomas, el mecanismo para agregar un mixin a una clase es ligeramente diferente (en términos de sintaxis) del de herencia.

Especialmente en el contexto de Python, un mixin es una clase principal que proporciona funcionalidad a las subclases pero no está diseñada para ser instanciada por sí misma.

Lo que podría hacer que diga, "eso es solo herencia múltiple, no realmente un mixin" es si la clase que podría confundirse con un mixin realmente puede ser instanciada y utilizada, por lo que es una diferencia semántica y muy real.

Ejemplo de herencia múltiple

Este ejemplo, de la documentación , es un OrderedCounter:

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

Subclasifica tanto el Countercomo el OrderedDictdecollections módulo.

Ambos CounteryOrderedDict están destinados a ser instanciados y utilizados por sí mismos. Sin embargo, al subclasificarlos a ambos, podemos tener un contador ordenado y reutilizar el código en cada objeto.

Esta es una forma poderosa de reutilizar el código, pero también puede ser problemático. Si resulta que hay un error en uno de los objetos, arreglarlo sin cuidado podría crear un error en la subclase.

Ejemplo de un mixin

Los mixins generalmente se promueven como la forma de reutilizar el código sin posibles problemas de acoplamiento que podría tener la herencia múltiple cooperativa, como el OrderedCounter. Cuando usa mixins, usa una funcionalidad que no está tan estrechamente unida a los datos.

A diferencia del ejemplo anterior, un mixin no está destinado a usarse solo. Proporciona funcionalidad nueva o diferente.

Por ejemplo, la biblioteca estándar tiene un par de mixins en la socketserverbiblioteca .

Se pueden crear versiones de bifurcación y subprocesamiento de cada tipo de servidor utilizando estas clases mixtas. Por ejemplo, ThreadingUDPServer se crea de la siguiente manera:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

La clase mix-in es lo primero, ya que anula un método definido en UDPServer. Establecer los diversos atributos también cambia el comportamiento del mecanismo del servidor subyacente.

En este caso, los métodos mixin anulan los métodos en la UDPServerdefinición del objeto para permitir la concurrencia.

El método reemplazado parece ser process_requesty también proporciona otro método, process_request_thread. Aquí es del código fuente :

class ThreadingMixIn:
        """Mix-in class to handle each request in a new thread."""

        # Decides how threads will act upon termination of the
        # main process
        daemon_threads = False

        def process_request_thread(self, request, client_address):
            """Same as in BaseServer but as a thread.
            In addition, exception handling is done here.
            """
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)

        def process_request(self, request, client_address):
            """Start a new thread to process the request."""
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()

Un ejemplo contribuido

Este es un mixin principalmente para fines de demostración: la mayoría de los objetos evolucionarán más allá de la utilidad de esta repr:

class SimpleInitReprMixin(object):
    """mixin, don't instantiate - useful for classes instantiable
    by keyword arguments to their __init__ method.
    """
    __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
    def __repr__(self):
        kwarg_strings = []
        d = getattr(self, '__dict__', None)
        if d is not None:
            for k, v in d.items():
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        slots = getattr(self, '__slots__', None)
        if slots is not None:
            for k in slots:
                v = getattr(self, k, None)
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        return '{name}({kwargs})'.format(
          name=type(self).__name__,
          kwargs=', '.join(kwarg_strings)
          )

y el uso sería:

class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
    __slots__ = 'foo',
    def __init__(self, foo=None):
        self.foo = foo
        super(Foo, self).__init__()

Y uso:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)
Aaron Hall
fuente
11

Creo que ha habido algunas buenas explicaciones aquí, pero quería ofrecer otra perspectiva.

En Scala, puede hacer mixins como se ha descrito aquí, pero lo que es muy interesante es que los mixins en realidad se fusionan para crear un nuevo tipo de clase para heredar. En esencia, no heredas de múltiples clases / mixins, sino que generas un nuevo tipo de clase con todas las propiedades del mixin para heredar. Esto tiene sentido ya que Scala se basa en la JVM donde la herencia múltiple no es compatible actualmente (a partir de Java 8). Este tipo de clase mixin, por cierto, es un tipo especial llamado Rasgo en Scala.

Se insinúa en la forma en que se define una clase: la clase NewClass extiende FirstMixin con SecondMixin con ThirdMixin ...

No estoy seguro de si el intérprete de CPython hace lo mismo (mixin class-composition) pero no me sorprendería. Además, viniendo de un entorno C ++, no llamaría un ABC o 'interfaz' equivalente a un mixin: es un concepto similar pero divergente en uso e implementación.

Silencioso
fuente
9

Aconsejaría contra las mezclas en el nuevo código de Python, si puede encontrar otra forma de evitarlo (como composición en lugar de herencia, o simplemente métodos de parches de mono en sus propias clases), eso no es mucho más esfuerzo.

En las clases de estilo antiguo, podría usar mezclas como una forma de obtener algunos métodos de otra clase. Pero en el mundo del nuevo estilo, todo, incluso la mezcla, hereda object. Eso significa que cualquier uso de herencia múltiple naturalmente presenta problemas de MRO .

Hay formas de hacer que MRO de herencia múltiple funcione en Python, especialmente la función super (), pero significa que tienes que hacer toda tu jerarquía de clases usando super (), y es considerablemente más difícil entender el flujo de control.

bobince
fuente
3
Desde la versión 2.3, Python utiliza la "resolución del método C3" explicada en El orden de resolución del método Python 2.3 o el Orden de resolución del método .
webwurst
11
Personalmente, tomaría mixins sobre parches de mono en la mayoría de los casos; Es más fácil razonar y seguir el código.
tdammers
55
Voto negativo Si bien su respuesta expresa una opinión válida sobre los estilos de desarrollo, realmente no aborda la pregunta real.
Ryan B. Lynch
8

Quizás un par de ejemplos ayudarán.

Si está creando una clase y desea que actúe como un diccionario, puede definir todos los diversos __ __métodos necesarios. Pero eso es un poco doloroso. Como alternativa, puede definir algunas y heredar (además de cualquier otra herencia) de UserDict.DictMixin(movido a collections.DictMixinpy3k). Esto tendrá el efecto de definir automáticamente todo el resto de la API del diccionario.

Un segundo ejemplo: el kit de herramientas GUI wxPython le permite crear controles de lista con múltiples columnas (como, por ejemplo, la visualización del archivo en el Explorador de Windows). Por defecto, estas listas son bastante básicas. Puede agregar funcionalidad adicional, como la capacidad de ordenar la lista por una columna en particular haciendo clic en el encabezado de la columna, heredando de ListCtrl y agregando los mixins apropiados.

John Fouhy
fuente
8

No es un ejemplo de Python, pero en el lenguaje de programación D el término mixinse usa para referirse a una construcción utilizada de la misma manera; agregando un montón de cosas a una clase.

En D (que por cierto no hace MI), esto se hace insertando una plantilla (piense en macros sintácticamente conscientes y seguras y estará cerca) en un ámbito. Esto permite una sola línea de código en una clase, estructura, función, módulo o lo que sea para expandirse a cualquier número de declaraciones.

BCS
fuente
2
Mixin es un término general, utilizado en D, Ruby, etc. Según Wikipedia, se originaron en los sistemas de lisp de la vieja escuela, y se documentaron por primera vez en 1983: en.wikipedia.org/wiki/…
Lee B
7

OP mencionó que él / ella nunca escuchó sobre mixin en C ++, tal vez porque se llaman Curiously Recurring Template Pattern (CRTP) en C ++. Además, @Ciro Santilli mencionó que mixin se implementa a través de la clase base abstracta en C ++. Si bien la clase base abstracta se puede usar para implementar mixin, es una exageración ya que la funcionalidad de la función virtual en tiempo de ejecución se puede lograr usando la plantilla en tiempo de compilación sin la sobrecarga de la búsqueda de tabla virtual en tiempo de ejecución.

El patrón CRTP se describe en detalle aquí

He convertido el ejemplo de Python en la respuesta de @Ciro Santilli a C ++ usando la clase de plantilla a continuación:

    #include <iostream>
    #include <assert.h>

    template <class T>
    class ComparableMixin {
    public:
        bool operator !=(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) == static_cast<T&>(other));
        }
        bool operator <(ComparableMixin &other) {
            return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
        }
        bool operator >(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
        }
        bool operator >=(ComparableMixin &other) {
            return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
        }
        protected:
            ComparableMixin() {}
    };

    class Integer: public ComparableMixin<Integer> {
    public:
     Integer(int i) {
         this->i = i;
     }
     int i;
     bool operator <=(Integer &other) {
         return (this->i <= other.i);
     }
     bool operator ==(Integer &other) {
         return (this->i == other.i);
     }
    };

int main() {

    Integer i(0) ;
    Integer j(1) ;
    //ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

EDITAR: Se agregó un constructor protegido en ComparableMixin para que solo se pueda heredar y no instanciar. Se actualizó el ejemplo para mostrar cómo el constructor protegido causará un error de compilación cuando se crea un objeto de ComparableMixin.

bigdata2
fuente
Mixins y CRTP no son exactamente lo mismo en C ++.
ashrasmun
6

Quizás un ejemplo de ruby ​​pueda ayudar:

Puede incluir el mixin Comparabley definir una función "<=>(other)", el mixin proporciona todas esas funciones:

<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)

Lo hace invocando <=>(other)y devolviendo el resultado correcto.

"instance <=> other"devuelve 0 si ambos objetos son iguales, menor que 0 si instancees mayor que othery mayor que 0 si otheres mayor.

Georg Schölly
fuente
Aquí hay una publicación que proporciona un mixin similar para Python. Aunque la sugerencia se define __lt__como base en lugar de __cmp__, la última de las cuales en realidad está en desuso y se desaconseja su uso. Para mí, parece más simple usar ese mixin en lugar de decoradores bastante complicados (parte de functools ), aunque este podría ser capaz de reaccionar más dinámicamente sobre las comparaciones que se proporcionan ...
Tobias Kienzler
6

mixin brinda una forma de agregar funcionalidad en una clase, es decir, puede interactuar con los métodos definidos en un módulo al incluir el módulo dentro de la clase deseada. Aunque ruby ​​no admite herencia múltiple, proporciona mixin como alternativa para lograrlo.

Aquí hay un ejemplo que explica cómo se logra la herencia múltiple usando mixin.

module A    # you create a module
    def a1  # lets have a method 'a1' in it
    end
    def a2  # Another method 'a2'
    end
end

module B    # let's say we have another module
    def b1  # A method 'b1'
    end
    def b2  #another method b2
    end
end

class Sample    # we create a class 'Sample'
    include A   # including module 'A' in the class 'Sample' (mixin)
    include B   # including module B as well

    def S1      #class 'Sample' contains a method 's1'
    end
end

samp = Sample.new    # creating an instance object 'samp'

# we can access methods from module A and B in our class(power of mixin)

samp.a1     # accessing method 'a1' from module A
samp.a2     # accessing method 'a2' from module A
samp.b1     # accessing method 'b1' from module B
samp.b2     # accessing method 'a2' from module B
samp.s1     # accessing method 's1' inside the class Sample
Akash Soti
fuente
44
¿Cuál es la diferencia entre esto y la herencia múltiple en general?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
La diferencia es que no puede crear instancias a partir de módulos, pero si no hay diferenciación entre clases generales y módulos, los mixins no son algo explícito y es difícil entender dónde hay una clase general y dónde hay un mixin
ka8725
Entonces, ¿en Ruby los mixins son solo clases que no se pueden instanciar, sino que se deben usar para la herencia múltiple?
Trilarion
6

Acabo de usar un Python Mixin para implementar pruebas unitarias para Python Milters. Normalmente, un milter habla con una MTA, lo que dificulta las pruebas unitarias. La mezcla de prueba anula los métodos que se comunican con el MTA y, en su lugar, crean un entorno simulado impulsado por casos de prueba.

Entonces, tomas una aplicación de milter no modificada, como spfmilter, y mixin TestBase, así:

class TestMilter(TestBase,spfmilter.spfMilter):
  def __init__(self):
    TestBase.__init__(self)
    spfmilter.config = spfmilter.Config()
    spfmilter.config.access_file = 'test/access.db'
    spfmilter.spfMilter.__init__(self)

Luego, use TestMilter en los casos de prueba para la aplicación milter:

def testPass(self):
  milter = TestMilter()
  rc = milter.connect('mail.example.com',ip='192.0.2.1')
  self.assertEqual(rc,Milter.CONTINUE)
  rc = milter.feedMsg('test1',sender='[email protected]')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup

Stuart Gathman
fuente
4

Creo que las respuestas anteriores definieron muy bien lo que son los MixIns . Sin embargo, para comprenderlos mejor, podría ser útil comparar MixIns con clases e interfaces abstractas desde la perspectiva del código / implementación:

1. Clase abstracta

  • Clase que necesita contener uno o más métodos abstractos

  • La clase abstracta puede contener estados (variables de instancia) y métodos no abstractos

2. interfaz

  • La interfaz contiene solo métodos abstractos (sin métodos no abstractos y sin estado interno)

3. MixIns

  • MixIns (como las interfaces) no contienen estado interno (variables de instancia)
  • MixIns contiene uno o más métodos no abstractos ( pueden contener métodos no abstractos a diferencia de las interfaces)

En, por ejemplo, Python, estas son solo convenciones, porque todo lo anterior se define como classes. Sin embargo, la característica común de ambas clases abstractas, interfaces y MixIns es que no deberían existir por sí mismas, es decir, no deberían ser instanciadas.

Tomasz Bartkowiak
fuente
3

Leí que tienes un fondo ac #. Entonces, un buen punto de partida podría ser una implementación mixin para .NET.

Es posible que desee consultar el proyecto codeplex en http://remix.codeplex.com/

Mire el enlace del simposio lang.net para obtener una descripción general. Todavía hay más por venir en la documentación en la página de codeplex.

saludos Stefan

Stefan Papp
fuente