web-dev-qa-db-fra.com

Comment les mixins ou les traits sont-ils meilleurs que l'héritage multiple simple?

C++ a un héritage multiple simple, de nombreux modèles de langage l'interdisent comme dangereux. Mais certains langages comme Ruby et PHP utilisent une syntaxe étrange pour faire la même chose et l'appellent mixins ou traits. J'ai entendu à plusieurs reprises que les mixins/traits sont plus difficiles) à l'abus que l'héritage multiple simple.

Qu'est-ce qui les rend spécifiquement moins dangereux? Y a-t-il quelque chose qui n'est pas possible avec les mixins/traits mais possible avec l'héritage multiple de style C++? Est-il possible de rencontrer le problème des diamants avec eux?

Cela semble comme si nous utilisions l'héritage multiple mais juste en faisant des excuses que ce sont des mixins/traits afin que nous puissions les utiliser.

57
Gherman

Il existe un certain nombre de problèmes avec l'héritage multiple lorsqu'il est utilisé avec des classes à part entière, mais ils tournent tous autour de ambiguïté.

L'ambiguïté se manifeste de différentes manières:

  1. Si vous avez deux classes de base avec le même champ x, et que le type dérivé demande x, qu'obtient-il?
    • Si les deux variables x ont des types incongruents, vous pouvez en déduire.
    • S'ils sont du même type, vous pouvez essayer de les fusionner dans la même variable.
    • Vous pouvez toujours les exposer comme des noms complètement qualifiés étranges.
  2. Si vous avez deux classes de base avec la même fonction f avec des signatures identiques et que quelqu'un appelle f, qui est appelé?
    • Et si les deux classes de base partagent un autre ancêtre virtuel commun (le problème du diamant).
    • Et si la fonction a des signatures différentes mais compatibles?
  3. Lorsque vous construisez une classe avec deux classes de base, quel constructeur des classes de base est appelé en premier? Lorsque vous détruisez l'objet, qui est tué?
  4. Lorsque vous disposez l'objet en mémoire, comment procédez-vous de manière cohérente?
  5. Comment gérez-vous tous ces cas avec 3 classes de base? dix?

Et cela ignore des choses comme la répartition dynamique, l'inférence de type, la correspondance de modèles et d'autres choses que je connais moins et qui deviennent plus difficiles lorsque le langage prend en charge l'héritage multiple de classes complètes.

Les traits ou Mix-ins (ou interfaces, ou ...) sont tous des constructions qui spécifiquement limite les capacités d'un type afin qu'il n'y ait aucune ambiguïté. Ils rarement possèdent quoi que ce soit eux-mêmes. Cela permet à la composition des types d'être plus fluide car il n'y a pas deux variables ou deux fonctions ... il y a une variable et une référence; une fonction et une signature. Le compilateur sait quoi faire.

L'autre approche courante adoptée consiste à forcer l'utilisateur à "construire" (ou mélanger) son type un à la fois. Au lieu que les classes de base soient des partenaires égaux dans le nouveau type, vous ajoutez un type à un autre - en remplaçant tout ce qui était là (généralement avec une syntaxe facultative pour renommer et/ou réexposer les bits remplacés).

Y a-t-il quelque chose qui n'est pas possible avec les mixins/traits mais possible avec l'héritage multiple de style C++?

Selon le langage - il devient généralement difficile ou impossible de fusionner implémentations de fonctions et de stockage pour les variables de plusieurs classes de base et de les exposer dans le type dérivé.

Est-il possible de rencontrer un problème de diamant avec eux?

Parfois, des variations moins sévères apparaissent en fonction de votre langue, mais généralement non. L'intérêt des traits est de briser ce genre d'ambiguïté.

32
Telastyn