Wie sind Mixins oder Merkmale besser als einfache Mehrfachvererbung?

54

C ++ hat eine einfache Mehrfachvererbung, viele Sprachentwürfe verbieten es als gefährlich. Einige Sprachen wie Ruby und PHP verwenden jedoch eine seltsame Syntax, um das Gleiche zu tun und nennen es Mixins oder Merkmale. Ich habe oft gehört, dass Mixins / Merkmale schwerer zu missbrauchen sind als Mehrfachvererbung.

Was macht sie konkret weniger gefährlich? Gibt es etwas, das mit Mixins / Merkmalen nicht möglich ist, aber mit Mehrfachvererbung im C ++ - Stil? Kann man mit ihnen auf das Diamantenproblem stoßen?

Dies scheint, als ob wir Mehrfachvererbung verwenden, aber nur Ausreden dafür machen, dass dies Mixins / Merkmale sind, damit wir sie verwenden können.

Gherman
quelle
7
Ich freue mich darauf, dass jemand eine gut geschriebene, nachdenkliche und gründliche Antwort auf diese Frage veröffentlicht.
Robert Harvey
7
Ruby und PHP haben keine Mixins und Eigenschaften eingeführt. Mixins wurden in Flavours (1980) und Traits in Squeak Smalltalk (2001) eingeführt. Flavours war die allererste objektorientierte Sprache mit Mehrfachvererbung und verwendete Mixins. C ++ wurde erst in Version 2.0 mehrfach vererbt, die 1989, neun Jahre nach Flavours, veröffentlicht wurde. Die Frage sollte also lauten: Flavours hat einfache Mixins, aber einige Sprachen wie C ++ führen eine seltsame Syntax ein, um dasselbe zu tun und es als Mehrfachvererbung zu bezeichnen.
Jörg W Mittag

Antworten:

31

Es gibt eine Reihe von Problemen mit der Mehrfachvererbung, wenn sie mit vollwertigen Klassen verwendet wird, aber alle drehen sich um Mehrdeutigkeiten .

Die Mehrdeutigkeit zeigt sich auf verschiedene Arten:

  1. Wenn Sie zwei Basisklassen mit demselben Feld xhaben und der abgeleitete Typ danach fragt x, was bekommt er?
    • Wenn die beiden xVariablen inkongruente Typen haben, können Sie daraus schließen.
    • Wenn sie vom selben Typ sind, können Sie versuchen, sie in derselben Variablen zusammenzuführen.
    • Sie können sie immer als seltsame, vollständig qualifizierte Namen ausgeben.
  2. Wenn Sie zwei Basisklassen mit der gleichen Funktion fund identischen Signaturen haben und jemand anruft f, welche wird angerufen?
    • Was ist, wenn die beiden Basisklassen einen gemeinsamen virtuellen Vorfahren haben (das Diamantenproblem)?
    • Was ist, wenn die Funktion unterschiedliche, aber kompatible Signaturen hat?
  3. Wenn Sie eine Klasse mit zwei Basisklassen erstellen, wird welcher Konstruktor der Basisklasse zuerst aufgerufen? Wenn Sie das Objekt zerstören, welches wird getötet?
  4. Wie machen Sie es konsequent, wenn Sie das Objekt im Speicher anordnen?
  5. Wie gehen Sie mit all diesen Fällen mit 3 Basisklassen um? 10?

Dabei werden Dinge wie dynamischer Versand, Typinferenz, Musterabgleich und andere Dinge, von denen ich weniger weiß, ignoriert, die schwieriger werden, wenn die Sprache die mehrfache Vererbung vollständiger Klassen unterstützt.

Merkmale oder Mix-Ins (oder Schnittstellen oder ...) sind alle Konstrukte, die die Fähigkeiten eines Typs spezifisch einschränken , so dass es keine Mehrdeutigkeiten gibt. Sie besitzen selbst selten etwas. Dadurch wird die Komposition von Typen reibungsloser, da es keine zwei Variablen oder zwei Funktionen gibt ... es gibt eine Variable und eine Referenz; eine Funktion und eine Signatur. Der Compiler weiß, was zu tun ist.

Der andere übliche Ansatz besteht darin, den Benutzer zu zwingen, ihren Typ einzeln zu "bauen" (oder zu mischen). Anstatt dass die Basisklassen gleichberechtigte Partner im neuen Typ sind, fügen Sie einen Typ zum anderen hinzu und überschreiben dabei alle vorhandenen Typen (normalerweise mit optionaler Syntax, um die überschriebenen Bits umzubenennen und / oder wieder verfügbar zu machen).

Gibt es etwas, das mit Mixins / Merkmalen nicht möglich ist, aber mit Mehrfachvererbung im C ++ - Stil?

Je nach Sprache wird es im Allgemeinen schwierig oder unmöglich, Implementierungen von Funktionen und Speicher für Variablen aus mehreren Basisklassen zusammenzuführen und im abgeleiteten Typ verfügbar zu machen.

Kann man mit ihnen auf Diamantenprobleme stoßen?

Gelegentlich treten je nach Sprache weniger schwerwiegende Abweichungen auf, in der Regel jedoch keine. Der gesamte Sinn der Merkmale besteht darin, diese Art von Zweideutigkeit zu überwinden.

Telastyn
quelle
Können Sie mir ein Beispiel für die Sprache / Technologie geben, die solche "Gebäudetypen" für eine Instanz zulässt?
Gherman
@german - Ich bin sicherlich kein Scala-Experte, aber ich verstehe, dass das withSchlüsselwort dort funktioniert.
Telastyn
"Konstrukte, die spezifisch die Fähigkeiten eines Typs einschränken, so dass es keine Mehrdeutigkeiten gibt": welche Art von Grenzen? Interfaces haben keine Variablen, das ist eine Grenze, aber Scala-Eigenschaften und Ruby-Module. Sind sie auf andere Weise eingeschränkt?
David Moles
@DavidMoles - das ist der übliche Weg. Ich habe auch Einschränkungen bei den Regeln für den virtuellen Versand festgestellt (denken Sie an die explizit implementierten Schnittstellen von C #).
Telastyn