Dans " Programming Python ", Mark Lutz mentionne "mixins". Je viens d’un milieu C/C++/C # et je n’ai jamais entendu le terme auparavant. Qu'est-ce qu'un mixin?
En lisant entre les lignes cet exemple (auquel j'ai lié parce que c'est assez long), je suppose que c'est un cas d'utilisation de l'héritage multiple pour étendre une classe, par opposition à un sous-classement "correct". Est-ce correct?
Pourquoi voudrais-je faire cela plutôt que de mettre la nouvelle fonctionnalité dans une sous-classe? D'ailleurs, pourquoi une approche mixin/héritage multiple serait-elle préférable à une composition?
Qu'est-ce qui sépare un mixin d'un héritage multiple? Est-ce juste une question de sémantique?
Un mixin est un type particulier d'héritage multiple. Il y a deux situations principales où les mixins sont utilisés:
Pour un exemple de numéro un, considérons système de demande et de réponse de werkzeug . Je peux créer un ancien objet de requête en disant:
from werkzeug import BaseRequest
class Request(BaseRequest):
pass
Si je veux ajouter accepter le support d'en-tête, je le ferais
from werkzeug import BaseRequest, AcceptMixin
class Request(AcceptMixin, BaseRequest):
pass
Si je voulais créer un objet de requête prenant en charge les en-têtes d'acceptation, les étiquettes, l'authentification et l'agent utilisateur, je pouvais le faire:
from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin
class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
pass
La différence est subtile, mais dans les exemples ci-dessus, les classes mixin n'étaient pas conçues pour être autonomes. Dans l'héritage multiple plus traditionnel, la AuthenticationMixin
(par exemple) ressemblerait probablement davantage à Authenticator
. C'est-à-dire que la classe serait probablement conçue pour être autonome.
Tout d'abord, vous devez noter que les mixins n'existent que dans des langues à héritages multiples. Vous ne pouvez pas faire de mixin dans Java ou en C #.
Fondamentalement, un mixin est un type de base autonome qui fournit une fonctionnalité limitée et une résonance polymorphe à une classe enfant. Si vous pensez en C #, pensez à une interface que vous n'avez pas à implémenter car elle est déjà implémentée; vous en héritez et profitez de ses fonctionnalités.
Les mixins ont généralement une portée étroite et ne sont pas destinés à être étendus.
[modifier - pour pourquoi:]
Je suppose que je devrais expliquer pourquoi, puisque vous avez demandé. Le gros avantage est que vous n'avez pas à le faire vous-même, encore et encore. En C #, le plus grand endroit où un mixin pourrait bénéficier pourrait être le Modèle d'élimination . Chaque fois que vous implémentez IDisposable, vous voulez presque toujours suivre le même schéma, mais vous finissez par écrire et réécrire le même code de base avec des variations mineures. S'il existait un mixage d'élimination extensible, vous pourriez économiser beaucoup de frappe.
[edit 2 - pour répondre à vos autres questions]
Qu'est-ce qui sépare un mixin d'un héritage multiple? Est-ce juste une question de sémantique?
Oui. La différence entre un héritage multiple mixte et standard multiple n’est qu’une question de sémantique; une classe ayant plusieurs héritages peut utiliser un mixin dans le cadre de cet héritage multiple.
L'intérêt d'un mixin est de créer un type qui peut être "mélangé" à tout autre type via l'héritage sans affecter le type hérité tout en offrant des fonctionnalités intéressantes pour ce type.
Encore une fois, pensez à une interface déjà implémentée.
Personnellement, je n'utilise pas de mixins car je développe principalement dans un langage qui ne les prend pas en charge. J'ai donc beaucoup de mal à trouver un exemple décent qui fournira simplement ce "ahah!". moment pour vous. Mais j'essaierai encore. Je vais utiliser un exemple artificiel - la plupart des langues fournissent déjà la fonctionnalité d'une manière ou d'une autre - mais cela, espérons-le, expliquera comment les mixins sont censés être créés et utilisés. Voici:
Supposons que vous ayez un type que vous voulez pouvoir sérialiser vers et depuis XML. Vous voulez que le type fournisse une méthode "ToXML" qui retourne une chaîne contenant un fragment XML avec les valeurs de données du type, et un "FromXML" qui permet au type de reconstruire ses valeurs de données à partir d'un fragment XML dans une chaîne. Encore une fois, il s’agit d’un exemple artificiel. Vous pouvez donc utiliser un flux de fichiers ou une classe XML Writer de la bibliothèque d’exécution de votre langue ... peu importe. Le fait est que vous souhaitez sérialiser votre objet en XML et récupérer un nouvel objet à partir de XML.
L'autre point important dans cet exemple est que vous souhaitez le faire de manière générique. Vous ne voulez pas avoir à implémenter les méthodes "ToXML" et "FromXML" pour chaque type que vous souhaitez sérialiser, vous voulez des moyens génériques pour vous assurer que votre type le fera et que cela fonctionne. Vous voulez réutiliser le code.
Si votre langue le prend en charge, vous pouvez créer le mixin XmlSerializable pour effectuer votre travail à votre place. Ce type implémenterait les méthodes ToXML et FromXML. En utilisant un mécanisme qui n’a pas d’importance dans cet exemple, il serait capable de collecter toutes les données nécessaires, quel que soit le type avec lequel il est mélangé, pour créer le fragment XML renvoyé par ToXML. Il serait tout aussi capable de restaurer ces données lorsque FromXML est utilisé. appelé.
Et c'est tout. Pour l'utiliser, tout type devant être sérialisé en XML hériterait de XmlSerializable. Chaque fois que vous avez besoin de sérialiser ou de désérialiser ce type, vous appelez simplement ToXML ou FromXML. En fait, étant donné que XmlSerializable est un type à part entière et polymorphe, vous pourriez concevoir un sérialiseur de document qui ne connaisse rien de votre type d'origine, acceptant uniquement, par exemple, un tableau de types XmlSerializable.
Imaginez maintenant que vous utilisiez ce scénario pour d'autres tâches, telles que la création d'un mixeur garantissant que chaque classe le mélangeant dans les journaux de chaque appel de méthode, ou un mixeur fournissant une transaction au type qui le mélange. La liste peut s'allonger.
Si vous considérez simplement un mixin comme un petit type de base conçu pour ajouter une petite quantité de fonctionnalités à un type sans affecter autrement ce type, vous êtes alors doré.
J'espère. :)
Cette réponse vise à expliquer les mixins avec des exemples qui sont:
autonome : bref, sans avoir besoin de connaître les bibliothèques pour comprendre l'exemple.
en Python , pas dans d'autres langues.
Il est compréhensible qu'il y ait eu des exemples d'autres langages tels que Ruby car le terme est beaucoup plus courant dans ces langages, mais il s'agit d'un fil Python.
Il examinera également la question controversée:
Un héritage multiple est-il nécessaire ou non pour caractériser un mixin?
Définitions
Je n'ai pas encore vu la citation d'une source "faisant autorité" indiquant clairement ce qu'est un mixin en Python.
J'ai vu 2 définitions possibles d'un mixin (si elles doivent être considérées comme différentes des autres concepts similaires tels que les classes de base abstraites), et les gens ne sont pas tout à fait d'accord sur celle qui est correcte.
Le consensus peut varier d'une langue à l'autre.
Définition 1: pas d'héritage multiple
Un mixin est une classe telle qu'une méthode de la classe utilise une méthode qui n'est pas définie dans la classe.
Par conséquent, la classe n'est pas destinée à être instanciée, mais sert plutôt de classe de base. Sinon, l'instance aurait des méthodes qui ne peuvent pas être appelées sans déclencher une exception.
Une contrainte que certaines sources ajoutent est que la classe peut ne pas contenir de données, mais uniquement des méthodes, mais je ne vois pas pourquoi cela est nécessaire. En pratique cependant, de nombreux mixins utiles ne contiennent aucune donnée et les classes de base sans données sont plus simples à utiliser.
Un exemple classique est l'implémentation de tous les opérateurs de comparaison à partir de <=
et ==
seulement:
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
Cet exemple particulier aurait pu être réalisé via le décorateur functools.total_ordering()
, mais le jeu ici était de réinventer la roue:
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)
Définition 2: héritage multiple
Un mixin est un modèle de conception dans lequel une méthode d'une classe de base utilise une méthode qu'elle ne définit pas, et cette méthode est censée être implémentée par ne autre classe de base, et non par la méthode dérivée comme dans la définition 1 .
Le terme mixin class fait référence aux classes de base destinées à être utilisées dans ce modèle de conception (celles qui utilisent la méthode ou celles qui l'implémentent?)
Il n'est pas facile de décider si une classe donnée est un mixin ou non: la méthode pourrait être simplement implémentée sur la classe dérivée, auquel cas nous revenons à la Définition 1. Vous devez prendre en compte les intentions de l'auteur.
Ce modèle est intéressant car il est possible de recombiner des fonctionnalités avec différents choix de classes de 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
Autorité _ occurrences Python
Au documentatiton officiel pour collections.abc , la documentation utilise explicitement le terme Méthodes Mixin.
Il déclare que si une classe:
__next__
Iterator
alors la classe obtient un __iter__
méthode mixin gratuitement.
Par conséquent, au moins sur ce point de la documentation, mixin ne nécessite pas d'héritage multiple et est cohérent avec la définition 1.
La documentation peut bien sûr être contradictoire à différents moments, et d’autres bibliothèques Python importantes pourraient utiliser l’autre définition dans leur documentation.
Cette page utilise également le terme Set mixin
, qui suggère clairement que des classes telles que Set
et Iterator
peuvent être appelées classes Mixin.
Dans d'autres langues
Ruby: Clairement, ne nécessite pas d'héritage multiple pour mixin, comme mentionné dans les principaux ouvrages de référence tels que Programming Ruby et le langage de programmation Ruby
C++: une méthode non implémentée est une méthode virtuelle pure.
La définition 1 coïncide avec la définition d'une classe abstraite (une classe qui a une méthode virtuelle pure). Cette classe ne peut pas être instanciée.
La définition 2 est possible avec l'héritage virtuel: Héritage multiple de deux classes dérivées
Je les considère comme un moyen discipliné d’utiliser l’héritage multiple - car au final, un mixin est juste une autre classe python qui respecte (peut-être) les conventions relatives aux classes appelées mixins.
Ma compréhension des conventions qui régissent ce que vous appelleriez un Mixin est qu’un Mixin:
object
(en Python)De cette façon, cela limite la complexité potentielle de l'héritage multiple et facilite le suivi du flux de votre programme en limitant l'orientation de votre programme (par rapport à l'héritage multiple complet). Ils sont similaires à modules Ruby .
Si je veux ajouter des variables d'instance (avec plus de souplesse que ne le permet un héritage unique), alors j'ai tendance à préférer la composition.
Cela dit, j'ai vu des classes appelées XYZMixin qui ont des variables d'instance.
Mixins est un concept de programmation dans lequel la classe fournit des fonctionnalités mais n’est pas destiné à être utilisé pour l’instanciation. L'objectif principal de Mixins est de fournir des fonctionnalités autonomes et il serait préférable que les mixins eux-mêmes n'aient pas d'héritage avec d'autres mixins et évitent également l'état. Dans des langages tels que Ruby, il existe un support de langage direct, mais pas pour Python. Cependant, vous pouvez utiliser l'héritage multi-classes pour exécuter la fonctionnalité fournie dans Python.
J'ai regardé cette vidéo http://www.youtube.com/watch?v=v_uKI2NOLEM pour comprendre les bases du mixins. Il est très utile pour un débutant de comprendre les bases de mixins, leur fonctionnement et les problèmes que vous pourriez rencontrer lors de leur mise en oeuvre.
Wikipedia est toujours le meilleur: http://en.wikipedia.org/wiki/Mixin
Qu'est-ce qui sépare un mixin d'un héritage multiple? Est-ce juste une question de sémantique?
Un mixin est une forme limitée d'héritage multiple. Dans certaines langues, le mécanisme pour ajouter un mixin à une classe est légèrement différent (en termes de syntaxe) de celui de l'héritage.
Dans le contexte de Python en particulier, un mixin est une classe parent qui fournit des fonctionnalités aux sous-classes, mais n'est pas destinée à être instanciée elle-même.
Ce qui pourrait vous amener à dire, "ce n'est qu'un héritage multiple, pas vraiment un mixin", c'est si la classe qui pourrait être confondue pour un mixin peut réellement être instanciée et utilisée - il s'agit donc bien d'une différence sémantique et très réelle.
Cet exemple, tiré de la documentation , est 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),)
Il sous-classe à la fois les variables Counter
et OrderedDict
du module collections
.
Les variables Counter
et OrderedDict
sont destinées à être instanciées et utilisées seules. Cependant, en les classant tous les deux, nous pouvons avoir un compteur ordonné qui réutilise le code dans chaque objet.
C'est un moyen puissant de réutiliser le code, mais cela peut aussi être problématique. S'il s'avère qu'il y a un bogue dans l'un des objets, le résoudre sans précaution peut créer un bogue dans la sous-classe.
Les mixins sont généralement présentés comme le moyen d'obtenir la réutilisation du code sans les problèmes de couplage potentiels d'un héritage multiple coopératif, tel que OrderedCounter. Lorsque vous utilisez des mixins, vous utilisez des fonctionnalités qui ne sont pas aussi étroitement liées aux données.
Contrairement à l'exemple ci-dessus, un mixin n'est pas destiné à être utilisé seul. Il fournit des fonctionnalités nouvelles ou différentes.
Par exemple, la bibliothèque standard a un couple de mixins dans la bibliothèque socketserver
.
Des versions en fork et en thread de chaque type de serveur peuvent être créées à l'aide de ces classes mix-in. Par exemple, ThreadingUDPServer est créé comme suit:
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
La classe mix-in vient en premier, car elle remplace une méthode définie dans UDPServer. La définition des divers attributs modifie également le comportement du mécanisme de serveur sous-jacent.
Dans ce cas, les méthodes mixin remplacent les méthodes de la définition d'objet UDPServer
pour permettre l'accès simultané.
La méthode remplacée semble être process_request
et fournit également une autre méthode, process_request_thread
. La voici à partir du code source :
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()
C'est un mix qui est principalement utilisé à des fins de démonstration - la plupart des objets évolueront au-delà de l'utilité de ce 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)
)
et l'utilisation serait:
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__()
Et utilisation:
>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)
Je vous déconseille les combinaisons dans le nouveau code Python, si vous pouvez trouver un autre moyen de le contourner (tel que la composition au lieu de l'héritage ou simplement des méthodes de correction par singe dans vos propres classes). ce n'est pas beaucoup plus d'effort.
Dans les classes de style ancien, vous pouvez utiliser des combinaisons pour récupérer quelques méthodes d'une autre classe. Mais dans le monde du nouveau style, tout, même le mixage, hérite de object
. Cela signifie que toute utilisation de l'héritage multiple introduit naturellement problèmes de MRO .
Il existe des moyens de gérer la MRO à héritages multiples en Python, notamment la fonction super (), mais cela signifie que vous devez hiérarchiser toute la classe à l'aide de super (), et il est beaucoup plus difficile de comprendre le flux de contrôle.
Je pense qu’il ya eu de bonnes explications ici mais je voulais donner un autre point de vue.
En Scala, vous pouvez faire des mixins comme cela a été décrit ici, mais ce qui est très intéressant, c’est que les mixins sont en réalité "fusionnés" pour créer un nouveau type de classe à partir duquel hériter. En substance, vous n'héritez pas de plusieurs classes/mixins, mais générez un nouveau type de classe avec toutes les propriétés du mixin dont vous souhaitez hériter. Cela a du sens puisque Scala est basé sur la machine virtuelle Java où l'héritage multiple n'est pas actuellement pris en charge (à partir de Java 8). Ce type de classe mixin est d'ailleurs un type spécial appelé Trait dans Scala.
C'est ce qui est suggéré dans la définition d'une classe: la classe NewClass étend FirstMixin à SecondMixin à ThirdMixin ...
Je ne sais pas si l'interprète CPython fait la même chose (composition de classe mixin) mais je ne serais pas surpris. De plus, venant d'un arrière-plan C++, je n'appellerais pas un ABC ou une "interface" équivalente à un mixin - c'est un concept similaire mais d'utilisation et de mise en œuvre divergentes.
Peut-être que quelques exemples aideront.
Si vous construisez une classe et que vous souhaitez qu'elle agisse comme un dictionnaire, vous pouvez définir toutes les méthodes __ __
nécessaires. Mais c'est un peu pénible. Au lieu de cela, vous pouvez simplement en définir quelques-uns et hériter (en plus de tout autre héritage) de UserDict.DictMixin
(déplacé vers collections.DictMixin
dans py3k). Cela aura pour effet de définir automatiquement tout le reste de l’API du dictionnaire.
Un deuxième exemple: le toolkit graphique wxPython vous permet de créer des contrôles de liste avec plusieurs colonnes (comme, par exemple, l’affichage du fichier dans l’explorateur Windows). Par défaut, ces listes sont assez basiques. Vous pouvez ajouter des fonctionnalités supplémentaires, telles que la possibilité de trier la liste en fonction d'une colonne en cliquant sur l'en-tête de la colonne, en héritant de ListCtrl et en ajoutant les mixins appropriés.
Ce n'est pas un exemple Python, mais le langage de programmation D , le terme mixin
est utilisé pour désigner une construction utilisée de la même manière; ajouter un tas de choses à une classe.
En D (ce qui d'ailleurs ne fait pas MI), ceci est fait en insérant un modèle (pensez à des macros sûres du point de vue syntaxique et vous serez proches) dans une portée. Cela permet à une seule ligne de code dans une classe, une structure, une fonction, un module ou autre de se développer en un nombre quelconque de déclarations.
Peut-être qu'un exemple de Ruby peut aider:
Vous pouvez inclure le mixin Comparable
et définir une fonction "<=>(other)"
, le mixin fournit toutes les fonctions suivantes:
<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)
Pour ce faire, il invoque <=>(other)
et donne le résultat voulu.
"instance <=> other"
renvoie 0 si les deux objets sont égaux, inférieur à 0 si instance
est supérieur à other
et supérieur à 0 si other
est plus grand.
mixin donne un moyen d’ajouter des fonctionnalités dans une classe, c’est-à-dire que vous pouvez interagir avec les méthodes définies dans un module en incluant le module dans la classe souhaitée. Bien que Ruby ne prenne pas en charge l'héritage multiple, il propose le mixin comme alternative.
voici un exemple qui explique comment l'héritage multiple est réalisé à l'aide de 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
Je viens d'utiliser un python mixin pour implémenter les tests unitaires pour python milters. Normalement, un milter parle à un MTA, ce qui rend les tests unitaires difficiles. Le mixage de test remplace les méthodes qui parlent au MTA et crée un environnement simulé basé sur des scénarios de test.
Donc, vous prenez une application de milter non modifiée, comme spfmilter, et mixin TestBase, comme ceci:
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)
Ensuite, utilisez TestMilter dans les cas de test pour l’application 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
OP a mentionné qu’il n’avait jamais entendu parler de mixin en C++, peut-être parce qu’ils sont appelés CRTP (Curually Recurrents Template Pattern) en C++. De plus, @Ciro Santilli a mentionné que mixin est implémenté via une classe de base abstraite en C++. Bien que la classe de base abstraite puisse être utilisée pour implémenter mixin, il s'agit d'un excès, car la fonctionnalité de la fonction virtuelle au moment de l'exécution peut être obtenue à l'aide d'un modèle lors de la compilation sans la surcharge de la recherche de table virtuelle au moment de l'exécution.
Le modèle CRTP est décrit en détail ici
J'ai converti l'exemple python de la réponse de @Ciro Santilli en C++ à l'aide de la classe de modèle ci-dessous:
#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: ajout du constructeur protégé dans ComparableMixin afin qu’il ne puisse être hérité que et non instancié. Mise à jour de l'exemple pour montrer comment le constructeur protégé provoquera une erreur de compilation lorsqu'un objet de ComparableMixin est créé.
J'ai lu que vous avez un fond c #. Donc, un bon point de départ pourrait être une implémentation mixin pour .NET.
Vous voudrez peut-être consulter le projet codeplex à l’adresse http://remix.codeplex.com/
Regardez le lien lang.net Symposium pour avoir un aperçu. Il y a encore beaucoup à venir dans la documentation sur la page codeplex.
salutations Stefan