Mixin vs. Vererbung
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 54 von 110.
Nachdem Sie nun gelernt haben, wie Mixins funktionieren, lassen Sie uns klären, wann man sie anstelle von Vererbung einsetzt. Der entscheidende Unterschied liegt in der Beziehung, die sie ausdrücken.
Vererbung stellt eine „is-a“-Beziehung dar. Ein Dog ist ein Animal. Die Unterklasse ist eine spezialisierte Version der Elternklasse, und man kann nur eine Klasse erweitern.
Mixins hingegen stellen eine „Kann-Beziehung“ dar. Ein Dog kann schwimmen, aber Schwimmen ist nicht das, was einen Hund definiert.
// Vererbung: Dog IST ein Animal
class Animal {
String name;
Animal(this.name);
}
class Dog extends Animal {
Dog(String name) : super(name);
}
// Mixin: Dog KANN schwimmen (wird aber nicht durch Schwimmen definiert)
mixin Swimming {
void swim() => print('Swimming');
}
class Dog extends Animal with Swimming {
Dog(String name) : super(name);
}Verwenden Sie Vererbung, wenn Klassen eine grundlegende Identität teilen und Sie Konstruktoren in der Elternklasse benötigen. Verwenden Sie Mixins, wenn Sie Klassen Funktionen hinzufügen möchten, die keinen gemeinsamen Vorfahren haben.
| Merkmal | Vererbung | Mixin |
|---|---|---|
| Beziehung | is-a | can-do |
| Beschränkung | Nur ein Elternteil | Mehrere Mixins |
| Konstruktoren | Erlaubt | Nicht erlaubt |
| Anwendungsfall | Spezialisierung | Gemeinsames Verhalten |
Das Beste daran? Sie können beides kombinieren – eine Klasse erweitern und mehrere Funktionen einmischen, wodurch Ihre Klassen genau die Features erhalten, die sie benötigen.
Aufgabe
EinfachLassen Sie uns ein Arbeitersystem aufbauen, das demonstriert, wann Vererbung gegenüber Mixins verwendet werden sollte. Sie werden eine Basisklasse für die "is-a"-Beziehung (ist-ein) und Mixins für "can-do"-Fähigkeiten (kann-etwas) erstellen und diese dann kombinieren, um zu zeigen, wie beide Ansätze zusammenarbeiten.
Sie werden Ihren Code in drei Dateien organisieren:
worker.dart: Definieren Sie Ihre Basisklasse, die die Kernidentität aller Arbeiter festlegt:- Eine
Worker-Klasse mit den EigenschaftenString nameundString department. Fügen Sie einen Konstruktor hinzu, der beide Werte entgegennimmt. Fügen Sie eine Methodeintroduce()hinzu, die[name] works in [department]ausgibt.
- Eine
skills.dart: Definieren Sie Mixins, die Fähigkeiten repräsentieren, die Arbeiter haben können (die "can-do"-Beziehungen):- Ein
CanCode-Mixin mit einer MethodewriteCode(), dieWriting code...ausgibt. - Ein
CanDesign-Mixin mit einer MethodecreateDesign(), dieCreating design...ausgibt. - Ein
CanManage-Mixin mit einer MethodeleadTeam(), dieLeading the team...ausgibt.
- Ein
main.dart: Importieren Sie beide Dateien und erstellen Sie spezialisierte Arbeitertypen, die Vererbung mit Mixins kombinieren:- Eine
Developer-Klasse, dieWorkererweitert und dasCanCode-Mixin verwendet. - Eine
TechLead-Klasse, dieWorkererweitert und sowohl dasCanCode- als auch dasCanManage-Mixin verwendet. - Erstellen Sie einen
Developernamens'Alice'in der Abteilung'Engineering'. - Erstellen Sie einen
TechLeadnamens'Bob'in der Abteilung'Engineering'. - Für Alice: Rufen Sie
introduce()und dannwriteCode()auf. - Geben Sie eine Leerzeile aus.
- Für Bob: Rufen Sie
introduce(), dannwriteCode()und schließlichleadTeam()auf.
- Eine
Beachten Sie, wie sowohl Developer als auch TechLead Arbeiter sind (Vererbung), aber unterschiedliche Dinge tun können (Mixins). Der TechLead kombiniert mehrere Fähigkeiten, ohne Mehrfachvererbung zu benötigen.
Erwartete Ausgabe:
Alice works in Engineering
Writing code...
Bob works in Engineering
Writing code...
Leading the team...Spickzettel
Vererbung stellt eine „ist-ein“-Beziehung dar (eine Unterklasse ist eine spezialisierte Version der Elternklasse), während Mixins eine „kann-etwas“-Beziehung darstellen (Hinzufügen von Fähigkeiten, ohne die Identität zu definieren).
// Inheritance: Dog IS an Animal
class Animal {
String name;
Animal(this.name);
}
class Dog extends Animal {
Dog(String name) : super(name);
}
// Mixin: Dog CAN swim
mixin Swimming {
void swim() => print('Swimming');
}
class Dog extends Animal with Swimming {
Dog(String name) : super(name);
}| Merkmal | Vererbung | Mixin |
|---|---|---|
| Beziehung | ist-ein | kann-etwas |
| Limit | Nur ein Elternteil | Mehrere Mixins |
| Konstruktoren | Erlaubt | Nicht erlaubt |
| Anwendungsfall | Spezialisierung | Gemeinsames Verhalten |
Sie können beide Ansätze kombinieren – eine Klasse erweitern und mehrere Fähigkeiten einmischen:
class TechLead extends Worker with CanCode, CanManage {
TechLead(String name, String department) : super(name, department);
}Probier es selbst
import 'worker.dart';
import 'skills.dart';
// TODO: Erstelle eine Developer-Klasse, die Worker erweitert und das CanCode-Mixin verwendet
// TODO: Erstelle eine TechLead-Klasse, die Worker erweitert und sowohl CanCode- als auch CanManage-Mixins verwendet
void main() {
// TODO: Erstelle einen Developer namens 'Alice' in der Abteilung 'Engineering'
// TODO: Rufe introduce() und dann writeCode() für Alice auf
// TODO: Gib eine Leerzeile aus
// TODO: Erstelle einen TechLead namens 'Bob' in der Abteilung 'Engineering'
// TODO: Rufe introduce(), writeCode() und dann leadTeam() für Bob auf
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor