O que é um mixin e por que eles são úteis?

955

Em " Programming Python ", Mark Lutz menciona "mixins". Eu sou de experiência em C / C ++ / C # e nunca ouvi o termo antes. O que é um mixin?

Lendo nas entrelinhas deste exemplo (ao qual vinculei porque é bastante longo), presumo que seja um caso de usar herança múltipla para estender uma classe em oposição à subclasse 'adequada'. Isto está certo?

Por que eu gostaria de fazer isso em vez de colocar a nova funcionalidade em uma subclasse? Por esse motivo, por que uma abordagem de mixin / herança múltipla seria melhor do que usar composição?

O que separa um mixin da herança múltipla? É apenas uma questão de semântica?

TarkaDaal
fonte

Respostas:

710

Um mixin é um tipo especial de herança múltipla. Existem duas situações principais em que os mixins são usados:

  1. Você deseja fornecer muitos recursos opcionais para uma classe.
  2. Você deseja usar um recurso específico em várias classes diferentes.

Para um exemplo do número um, considere o sistema de solicitação e resposta do werkzeug . Eu posso criar um objeto de solicitação antigo simples dizendo:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Se eu quiser adicionar suporte ao cabeçalho Accept, eu faria isso

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Se eu quisesse fazer um objeto de solicitação que suporte aceitar cabeçalhos, etags, autenticação e suporte ao agente do usuário, eu poderia fazer o seguinte:

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

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

A diferença é sutil, mas nos exemplos acima, as classes de mixin não foram criadas por conta própria. Na herança múltipla mais tradicional, o AuthenticationMixin(por exemplo) provavelmente seria algo mais parecido Authenticator. Ou seja, a classe provavelmente seria projetada para se sustentar por conta própria.

Jason Baker
fonte
123
Uma terceira situação é: você deseja fornecer muitos recursos (não opcionais) para uma classe, mas deseja os recursos em classes separadas (e em módulos separados) para que cada módulo seja sobre um recurso (comportamento). IOW, não para reutilização, mas para compartimentação.
Bootck #
60
Provavelmente não é um problema neste exemplo, mas geralmente você deseja colocar a classe base principal como o último elemento entre parênteses para criar a cadeia de herança: Request ==> Mixin ==> ... ==> BaseRequest. Veja aqui: ianlewis.org/en/mixins-and-python
hillel:
10
@hillel bom ponto, mas lembre-se de que o Python chamará os métodos das superclasses da esquerda para a direita (quando você precisar substituir o construtor, por exemplo).
Eliseu Monar dos Santos
9
Isso parece muito com o padrão de design do Decorator.
D-Jones
4
A 4ª situação é: já existe uma família existente de Parentclasse e Child1, Child2, ChildNsubclasses dentro de uma biblioteca parte 3, e você quer um comportamento personalizado para toda a família. Idealmente, você gostaria de adicionar esse comportamento Parente espero que o desenvolvedor da biblioteca de terceiros aceite sua solicitação de recebimento. Caso contrário, você terá que implementar seu próprio class NewBehaviorMixine, em seguida, definir um conjunto completo de classes de mensagens publicitárias, como class NewParent(NewBehaviorMixin, Parent): passe class NewChildN(NewBehaviorMixin, ChildN): pass, etc.: (PS Você conhece uma maneira melhor?)
RayLuo
240

Primeiro, você deve observar que os mixins existem apenas em idiomas de herança múltipla. Você não pode fazer um mixin em Java ou C #.

Basicamente, um mixin é um tipo de base independente que fornece funcionalidade limitada e ressonância polimórfica para uma classe filho. Se você está pensando em C #, pense em uma interface que você não precisa implementar porque ela já está implementada; você apenas herda e se beneficia de sua funcionalidade.

Mixins são tipicamente de escopo estreito e não devem ser estendidos.

[editar - por que:]

Suponho que devo abordar o porquê, desde que você perguntou. O grande benefício é que você não precisa fazer isso sozinho uma e outra vez. Em C #, o maior local em que um mixin poderia se beneficiar pode ser o padrão de Disposição . Sempre que você implementa o IDisposable, quase sempre deseja seguir o mesmo padrão, mas acaba escrevendo e reescrevendo o mesmo código básico com pequenas variações. Se houver uma mixagem descartável extensível, você poderá economizar bastante digitação extra.

[editar 2 - para responder suas outras perguntas]

O que separa um mixin da herança múltipla? É apenas uma questão de semântica?

Sim. A diferença entre uma mistura e herança múltipla padrão é apenas uma questão de semântica; uma classe que tem herança múltipla pode utilizar um mixin como parte dessa herança múltipla.

O objetivo de um mixin é criar um tipo que possa ser "misturado" a qualquer outro tipo por herança, sem afetar o tipo de herança e ainda oferecer algumas funcionalidades benéficas para esse tipo.

Mais uma vez, pense em uma interface que já esteja implementada.

Pessoalmente, não uso mixins, já que desenvolvo principalmente em uma linguagem que não os suporta, por isso estou tendo dificuldades para encontrar um exemplo decente que apenas forneça esse "ahah!" momento para você. Mas vou tentar novamente. Vou usar um exemplo que é artificial - a maioria das linguagens já fornece o recurso de uma maneira ou de outra -, mas espero que explique como os mixins devem ser criados e usados. Aqui vai:

Suponha que você tenha um tipo que deseja poder serializar de e para XML. Você deseja que o tipo forneça um método "ToXML" que retorne uma sequência contendo um fragmento XML com os valores de dados do tipo e um "FromXML" que permita ao tipo reconstruir seus valores de dados a partir de um fragmento XML em uma sequência. Novamente, este é um exemplo artificial, então talvez você use um fluxo de arquivos ou uma classe XML Writer da biblioteca de tempo de execução da sua linguagem ... qualquer que seja. O ponto é que você deseja serializar seu objeto para XML e obter um novo objeto de volta a partir de XML.

O outro ponto importante neste exemplo é que você deseja fazer isso de maneira genérica. Você não precisa implementar os métodos "ToXML" e "FromXML" para todos os tipos que deseja serializar; deseja alguns meios genéricos de garantir que seu tipo faça isso e funcione. Você deseja reutilizar o código.

Se o seu idioma o suportasse, você poderia criar o mixin XmlSerializable para fazer seu trabalho. Esse tipo implementaria os métodos ToXML e FromXML. Seria, usando algum mecanismo que não é importante para o exemplo, ser capaz de reunir todos os dados necessários de qualquer tipo com o qual é misturado para criar o fragmento XML retornado pelo ToXML e seria igualmente capaz de restaurar esses dados quando o FromXML for chamado.

E é isso. Para usá-lo, você teria qualquer tipo que precise ser serializado para XML herdado de XmlSerializable. Sempre que você precisar serializar ou desserializar esse tipo, basta chamar ToXML ou FromXML. De fato, como XmlSerializable é um tipo completo e polimórfico, é possível criar um serializador de documentos que não saiba nada sobre o seu tipo original, aceitando apenas, digamos, uma matriz de tipos XmlSerializable.

Agora imagine usar esse cenário para outras coisas, como criar um mixin que garanta que toda classe que o mescla registre todas as chamadas de método ou um mixin que forneça transacionalidade para o tipo que o mescla. A lista pode continuar.

Se você apenas pensa em um mixin como um pequeno tipo de base, projetado para adicionar uma pequena quantidade de funcionalidade a um tipo sem afetar esse tipo, então você é dourado.

Esperançosamente. :)

Randolpho
fonte
25
Ei, você gosta da frase "ressonância polimórfica"? Eu mesmo inventei. Eu acho que. Talvez eu ouvi-lo em algum lugar física ...
Randolpho
50
Discordo um pouco da sua primeira frase. Ruby é uma linguagem de herança única e mixins são a maneira de adicionar métodos a uma determinada classe sem herança de outra classe.
22909 Keltia
23
@Keltia: Eu acho que os mixin são - por definição - herança múltipla. No caso do Ruby, eles são um monkeypatch (ou qualquer outra coisa) e não um mixin adequado. O pessoal do Ruby pode chamá-lo de mixin, mas é um tipo diferente de coisa.
315/09 S.Lott
10
Na verdade, um mixin verdadeiro não pode usar herança múltipla. Um mixin inclui métodos, atributos etc. de uma classe em outra sem herdá-la. Isso tende a dar os benefícios da reutilização de código com o polimorfismo, mas deixa de fora os problemas que determinam a paternidade (o diamante da morte etc.) As linguagens de suporte ao mixin também tendem a permitir a inclusão parcial da classe mixin (as coisas estão começando a soar um pouco como aspectos agora).
Trevor
8
Para o registro, o Java agora suporta mixins com métodos padrão.
Shmosel
170

Esta resposta tem como objetivo explicar os mixins com exemplos que são:

  • independente : curto, sem a necessidade de conhecer nenhuma biblioteca para entender o exemplo.

  • em Python , não em outros idiomas.

    É compreensível que houvesse exemplos de outras linguagens, como Ruby, já que o termo é muito mais comum nessas linguagens, mas esse é um encadeamento Python .

Também deve considerar a questão controversa:

A herança múltipla é necessária ou não para caracterizar um mixin?

Definições

Ainda não vi uma citação de uma fonte "autorizada" dizendo claramente o que é um mixin no Python.

Eu já vi duas definições possíveis de um mixin (se elas devem ser consideradas diferentes de outros conceitos semelhantes, como classes básicas abstratas), e as pessoas não concordam inteiramente sobre qual delas está correta.

O consenso pode variar entre diferentes idiomas.

Definição 1: nenhuma herança múltipla

Um mixin é uma classe de tal forma que algum método da classe usa um método que não está definido na classe.

Portanto, a classe não deve ser instanciada, mas serve como uma classe base. Caso contrário, a instância teria métodos que não podem ser chamados sem gerar uma exceção.

Uma restrição que algumas fontes adicionam é que a classe pode não conter dados, apenas métodos, mas não vejo por que isso é necessário. Na prática, no entanto, muitos mixins úteis não possuem dados e as classes base sem dados são mais simples de usar.

Um exemplo clássico é a implementação de todos os operadores de comparação apenas <=e ==:

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 exemplo em particular poderia ter sido alcançado através do functools.total_ordering()decorador, mas o jogo aqui era reinventar a roda:

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)

Definição 2: herança múltipla

Um mixin é um padrão de design no qual algum método de uma classe base usa um método que não define, e esse método deve ser implementado por outra classe base , não pelo derivado, como na Definição 1.

O termo classe mixin refere-se a classes base que devem ser usadas nesse padrão de design (TODO aquelas que usam o método ou aquelas que o implementam?)

Não é fácil decidir se uma determinada classe é uma combinação ou não: o método pode ser implementado apenas na classe derivada; nesse caso, voltamos à definição 1. Você deve considerar as intenções do autor.

Esse padrão é interessante porque é possível recombinar funcionalidades com diferentes opções de classes 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

Ocorrências oficiais do Python

Na documentação oficial para coleções.abc, a documentação usa explicitamente o termo Métodos Mixin .

Ele afirma que se uma classe:

  • implementa __next__
  • herda de uma única classe Iterator

então a classe recebe um __iter__ método mixin de graça.

Portanto, pelo menos neste ponto da documentação, mixin não requer herança múltipla e é coerente com a Definição 1.

É claro que a documentação pode ser contraditória em pontos diferentes, e outras bibliotecas Python importantes podem estar usando a outra definição em sua documentação.

Esta página também usa o termo Set mixin, que sugere claramente que as classes gostam Sete Iteratorpodem ser chamadas de classes Mixin.

Em outros idiomas

  • Ruby: Claramente não requer herança múltipla para mixin, como mencionado nos principais livros de referência, como Programming Ruby e The Ruby programming Language

  • C ++: um método que não é implementado é um método virtual puro.

    A definição 1 coincide com a definição de uma classe abstrata (uma classe que possui um método virtual puro). Essa classe não pode ser instanciada.

    A definição 2 é possível com herança virtual: herança múltipla de duas classes derivadas

Ciro Santilli adicionou uma nova foto
fonte
37

Penso neles como uma maneira disciplinada de usar herança múltipla - porque, em última análise, um mixin é apenas outra classe python que (pode) seguir as convenções sobre classes chamadas mixins.

Meu entendimento das convenções que governam algo que você chamaria de Mixin é que um Mixin:

  • adiciona métodos, mas não variáveis ​​de instância (constantes de classe estão OK)
  • herda apenas de object(em Python)

Dessa forma, limita a complexidade potencial da herança múltipla e facilita razoavelmente o rastreamento do fluxo do seu programa, limitando para onde você deve procurar (em comparação com a herança múltipla completa). Eles são semelhantes aos módulos ruby .

Se eu quiser adicionar variáveis ​​de instância (com mais flexibilidade do que o permitido por herança única), então tendem a procurar composição.

Dito isto, vi classes chamadas XYZMixin que possuem variáveis ​​de instância.

Hamish Downer
fonte
30

Mixins é um conceito em Programação no qual a classe fornece funcionalidades, mas não deve ser usada para instanciação. O principal objetivo do Mixins é fornecer funcionalidades independentes e seria melhor se os próprios mixins não tivessem herança com outros mixins e também evitassem o estado. Em linguagens como Ruby, existe algum suporte direto à linguagem, mas para Python, não existe. No entanto, você pode usar a herança de várias classes para executar a funcionalidade fornecida no Python.

Eu assisti esse vídeo http://www.youtube.com/watch?v=v_uKI2NOLEM para entender o básico dos mixins. É bastante útil para um iniciante entender os conceitos básicos de mixins e como eles funcionam e os problemas que você pode enfrentar ao implementá-los.

A Wikipedia ainda é a melhor: http://en.wikipedia.org/wiki/Mixin

lakesh
fonte
29

O que separa um mixin da herança múltipla? É apenas uma questão de semântica?

Um mixin é uma forma limitada de herança múltipla. Em algumas linguagens, o mecanismo para adicionar um mixin a uma classe é um pouco diferente (em termos de sintaxe) do de herança.

Especialmente no contexto do Python, um mixin é uma classe pai que fornece funcionalidade às subclasses, mas não se destina a ser instanciado.

O que pode fazer você dizer: "isso é apenas herança múltipla, não realmente uma mixin" é se a classe que pode ser confundida para uma mixin pode realmente ser instanciada e usada - então, de fato, é uma diferença semântica e muito real.

Exemplo de herança múltipla

Este exemplo, da documentação , é um 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),)

Subclassifica o Countere o OrderedDictdo collectionsmódulo.

Ambos Countere OrderedDictdevem ser instanciados e usados ​​por conta própria. No entanto, ao subclassificar os dois, podemos ter um contador que é ordenado e reutiliza o código em cada objeto.

Essa é uma maneira poderosa de reutilizar código, mas também pode ser problemática. Se ocorrer um erro em um dos objetos, corrigi-lo sem cuidado pode criar um erro na subclasse.

Exemplo de um Mixin

Os mixins geralmente são promovidos como a maneira de obter a reutilização de código sem possíveis problemas de acoplamento que a herança múltipla cooperativa, como o OrderedCounter, poderia ter. Ao usar mixins, você usa funcionalidades que não são tão fortemente acopladas aos dados.

Ao contrário do exemplo acima, um mixin não se destina a ser usado por si só. Ele fornece funcionalidade nova ou diferente.

Por exemplo, a biblioteca padrão possui algumas mixins na socketserverbiblioteca .

Versões de fork e threading de cada tipo de servidor podem ser criadas usando essas classes mix-in. Por exemplo, ThreadingUDPServer é criado da seguinte maneira:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

A classe mix-in vem em primeiro lugar, pois substitui um método definido no UDPServer. A configuração dos vários atributos também altera o comportamento do mecanismo do servidor subjacente.

Nesse caso, os métodos mixin substituem os métodos na UDPServerdefinição de objeto para permitir a simultaneidade.

O método substituído parece ser process_requeste também fornece outro método process_request_thread,. Aqui está do código fonte :

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()

Um exemplo inventado

Esta é uma combinação que é principalmente para fins de demonstração - a maioria dos objetos evolui além da utilidade desse 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)
          )

e uso seria:

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__()

E uso:

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

Eu acho que houve algumas boas explicações aqui, mas eu queria fornecer outra perspectiva.

No Scala, você pode fazer mixins como foi descrito aqui, mas o que é muito interessante é que os mixins são realmente 'fundidos' para criar um novo tipo de classe da qual herdar. Em essência, você não herda de várias classes / mixins, mas gera um novo tipo de classe com todas as propriedades do mixin para herdar. Isso faz sentido, pois o Scala é baseado na JVM, na qual a herança múltipla não é atualmente suportada (a partir do Java 8). Esse tipo de classe mixin, a propósito, é um tipo especial chamado Trait in Scala.

É sugerido na maneira como uma classe é definida: a classe NewClass estende FirstMixin com SecondMixin com ThirdMixin ...

Não tenho certeza se o intérprete CPython faz o mesmo (mixin composição de classe), mas não ficaria surpreso. Além disso, vindo de um background em C ++, eu não chamaria um ABC ou 'interface' equivalente a um mixin - é um conceito semelhante, mas divergente em uso e implementação.

SilentDirge
fonte
9

Eu não recomendaria mix-ins no novo código Python, se você puder encontrar outra maneira de contornar isso (como composição em vez de herança, ou apenas métodos de correção de macaco em suas próprias classes) que não são muito mais esforço.

Nas aulas de estilo antigo, você pode usar os mix-ins como uma maneira de pegar alguns métodos de outra classe. Mas, no mundo do novo estilo, tudo, até o mix, herda object. Isso significa que qualquer uso de herança múltipla naturalmente introduz problemas de MRO .

Existem maneiras de fazer o MRO de herança múltipla funcionar em Python, principalmente a função super (), mas isso significa que você deve executar toda a hierarquia de classes usando super (), e é consideravelmente mais difícil entender o fluxo de controle.

bobince
fonte
3
Desde a versão 2.3, o Python usa a "resolução do método C3" explicada em Ordem de resolução de método do Python 2.3 ou Ordem de resolução de método .
Webwurst # 13/12
11
Pessoalmente, eu usaria mixins sobre remendos de macacos na maioria dos casos; é mais fácil argumentar e seguir o código.
usar o seguinte código
5
Votado. Embora sua resposta expresse uma opinião válida sobre estilos de desenvolvimento, você realmente não aborda a questão real.
22913 Ryan Ryan Lynch
8

Talvez alguns exemplos ajudem.

Se você está construindo uma classe e deseja que ela atue como um dicionário, você pode definir todos os vários __ __métodos necessários. Mas isso é um pouco de dor. Como alternativa, você pode apenas definir alguns e herdar (além de qualquer outra herança) de UserDict.DictMixin(movido paracollections.DictMixin em py3k). Isso terá o efeito de definir automaticamente todo o restante da API do dicionário.

Um segundo exemplo: o kit de ferramentas da GUI wxPython permite que você faça controles de lista com várias colunas (como, por exemplo, a exibição do arquivo no Windows Explorer). Por padrão, essas listas são bastante básicas. Você pode adicionar funcionalidades adicionais, como a capacidade de classificar a lista por uma coluna específica, clicando no cabeçalho da coluna, herdando do ListCtrl e adicionando mixins apropriados.

John Fouhy
fonte
8

Não é um exemplo de Python, mas na linguagem de programação D o termo mixiné usado para se referir a uma construção usada da mesma maneira; adicionando uma pilha de coisas a uma classe.

Em D (que, aliás, não executa MI), isso é feito inserindo um modelo (pense em macros sintaticamente conscientes e seguras e você estará próximo) em um escopo. Isso permite que uma única linha de código em uma classe, estrutura, função, módulo ou qualquer outra seja expandida para qualquer número de declarações.

BCS
fonte
2
Mixin é um termo geral, usado em D, Ruby etc. De acordo com a Wikipedia, eles se originaram em sistemas de cifras da velha escola e foram documentados pela primeira vez em 1983: en.wikipedia.org/wiki/…
Lee B
7

O OP mencionou que nunca ouviu falar de mixin em C ++, talvez seja porque eles são chamados CRTP (Curiously Recurring Template Pattern) em C ++. Além disso, @Ciro Santilli mencionou que o mixin é implementado via classe base abstrata em C ++. Embora a classe base abstrata possa ser usada para implementar mixin, é um exagero, pois a funcionalidade da função virtual em tempo de execução pode ser alcançada usando o modelo em tempo de compilação, sem a sobrecarga da pesquisa da tabela virtual em tempo de execução.

O padrão CRTP é descrito em detalhes aqui

Eu converti o exemplo python na resposta de @Ciro Santilli em C ++ usando a classe de modelo abaixo:

    #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;
}

EDIT: Adicionado construtor protegido no ComparableMixin para que ele possa ser herdado e não instanciado. Atualizado o exemplo para mostrar como o construtor protegido causará erro de compilação quando um objeto do ComparableMixin é criado.

bigdata2
fonte
Mixins e CRTP não são exatamente a mesma coisa em C ++.
ashrasmun
6

Talvez um exemplo do ruby ​​possa ajudar:

Você pode incluir o mixin Comparablee definir uma função "<=>(other)", o mixin fornece todas essas funções:

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

Faz isso invocando <=>(other) e devolvendo o resultado certo.

"instance <=> other"retorna 0 se os dois objetos forem iguais, menor que 0 se instancefor maior que othere maior que 0 se otherfor maior.

Georg Schölly
fonte
Aqui está um post que fornece um mixin semelhante para o Python. Embora a sugestão esteja definindo __lt__como base em vez de __cmp__, a última delas é realmente obsoleta e desencorajada de usar. Para mim, parece mais simples de usar que mixin em vez de bastante complicado decoradores (parte de functools ) - embora este pode ser capaz de reagir de forma mais dinâmica em que as comparações são fornecidos ...
Tobias KIENZLER
6

O mixin fornece uma maneira de adicionar funcionalidade em uma classe, ou seja, você pode interagir com os métodos definidos em um módulo, incluindo o módulo na classe desejada. Embora o ruby ​​não suporte herança múltipla, fornece mixin como uma alternativa para conseguir isso.

Aqui está um exemplo que explica como a herança múltipla é alcançada usando o 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
fonte
4
Qual é a diferença entre esta e herança múltipla em geral?
Ciro Santilli escreveu
A diferença é que você não é capaz de criar instâncias a partir de módulos, mas se não houver diferenciação entre classes e módulos gerais, os mixins não são explícitos e é difícil entender onde é uma classe geral e onde é um
mixin
Então, no Ruby, mixins são apenas classes que não podem ser instanciadas, mas devem ser usadas para herança múltipla?
Trilarion
6

Eu apenas usei um python mixin para implementar testes de unidade para ordenadores python. Normalmente, um milter conversa com um MTA, dificultando o teste de unidade. O mix de teste substitui os métodos que conversam com o MTA e cria um ambiente simulado orientado por casos de teste.

Portanto, você adota um aplicativo milter não modificado, como spfmilter, e mixin TestBase, assim:

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)

Em seguida, use TestMilter nos casos de teste para o aplicativo 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
fonte
4

Acho que as respostas anteriores definiram muito bem o que são os MixIns . No entanto, para entendê-los melhor, pode ser útil comparar MixIns com Classes e Interfaces Abstratas da perspectiva do código / implementação:

1. Classe Abstrata

  • Classe que precisa conter um ou mais métodos abstratos

  • A classe Abstract pode conter métodos de estado (variáveis ​​de instância) e não-abstratos

2. Interface

  • A interface contém apenas métodos abstratos (sem métodos não abstratos e sem estado interno)

3. MixIns

  • MixIns (como Interfaces) não contêm estado interno (variáveis ​​de instância)
  • MixIns contêm um ou mais métodos não abstratos (eles podem conter métodos não abstratos, diferentemente das interfaces)

No Python, por exemplo, essas são apenas convenções, porque todas as opções acima são definidas como classes. No entanto, o recurso comum das Classes Abstratas, Interfaces e MixIns é que elas não devem existir por si próprias, ou seja, não devem ser instanciadas.

Tomasz Bartkowiak
fonte
3

Eu li que você tem um histórico de ac #. Portanto, um bom ponto de partida pode ser uma implementação combinada para o .NET.

Você pode querer verificar o projeto codeplex em http://remix.codeplex.com/

Assista ao link do Simpósio lang.net para obter uma visão geral. Ainda há mais documentação disponível na página codeplex.

cumprimentos Stefan

Stefan Papp
fonte