Mixin vs. Interface
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 55 von 110.
Sie haben gesehen, wie Mixins im Vergleich zur Vererbung abschneiden. Lassen Sie uns nun Mixins mit Interfaces vergleichen, da beide es ermöglichen, mit mehreren Typen zu arbeiten. Der entscheidende Unterschied liegt darin, was sie bieten: Interfaces definieren einen Vertrag, während Mixins eine Implementierung bereitstellen.
Wenn Sie ein Interface implementieren, versprechen Sie, bestimmte Methoden bereitzustellen, aber Sie schreiben den gesamten Code selbst. Wenn Sie ein Mixin verwenden, erhalten Sie den tatsächlichen Code kostenlos.
// Interface-Ansatz - Sie müssen alles implementieren
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // Das schreiben Sie
}
}
// Mixin-Ansatz - Implementierung enthalten
mixin Printable {
void printInfo() {
print('Printing...'); // Bereits für Sie geschrieben
}
}
class Report with Printable {} // Erhält printInfo() automatischWann sollten Sie also was verwenden? Verwenden Sie ein Interface, wenn verschiedene Klassen dieselben Methodensignaturen, aber unterschiedliche Implementierungen benötigen. Verwenden Sie ein Mixin, wenn Klassen genau dasselbe Verhalten teilen sollen.
| Merkmal | Interface | Mixin |
|---|---|---|
| Bietet | Nur Vertrag | Vertrag + Code |
| Implementierung | Selbst geschrieben | Enthalten |
| Schlüsselwort | implements | with |
| Ideal für | Unterschiedliche Verhaltensweisen, gleiche API | Gemeinsames Verhalten |
Sie können sogar beide Ansätze kombinieren – verwenden Sie Interfaces, um zu definieren, was eine Klasse tun kann, und Mixins, um bei Bedarf gängige Implementierungen bereitzustellen. In Dart müssen Sie beim Kombinieren von beidem with immer vor implements setzen:
class MyClass with SomeMixin implements SomeInterface {
// with steht vor implements
}Aufgabe
EinfachLassen Sie uns ein Benachrichtigungssystem erstellen, das demonstriert, wann Schnittstellen (Interfaces) gegenüber Mixins verwendet werden sollten. Sie erstellen ein Interface für Benachrichtigungen, die unterschiedliche Implementierungen erfordern, und ein Mixin für gemeinsames Logging-Verhalten, das bei allen Benachrichtigungstypen identisch sein soll.
Sie werden Ihren Code in drei Dateien organisieren:
notifiable.dart: Definieren Sie eine abstrakte KlasseNotifiable, die als Interface fungiert. Sie sollte zwei abstrakte Methoden deklarieren:send(String message)undgetChannel(), die einenStringzurückgibt. Verschiedene Benachrichtigungstypen werden diese Methoden basierend auf ihrem spezifischen Kanal unterschiedlich implementieren.logging.dart: Erstellen Sie einLoggingMixin, das gemeinsames Verhalten bereitstellt, das alle Benachrichtigungen haben sollten. Fügen Sie eine MethodelogSent(String channel, String message)hinzu, die[LOG] Sent via [channel]: [message]ausgibt, und eine MethodelogTimestamp(), die[LOG] Timestamp recordedausgibt. Dieses Verhalten ist für alle Benachrichtigungstypen identisch, was es perfekt für ein Mixin macht.main.dart: Importieren Sie beide Dateien und erstellen Sie zwei Benachrichtigungsklassen, die das Interface mit dem Mixin kombinieren:- Eine
EmailNotificationKlasse, die dasLoggingMixin verwendet undNotifiableimplementiert. IhrgetChannel()sollte'Email'zurückgeben, und ihresend()Methode sollteSending email: [message]ausgeben, dannlogSent()mit dem Kanal und der Nachricht aufrufen und anschließendlogTimestamp()aufrufen. - Eine
SMSNotificationKlasse, die dasLoggingMixin verwendet undNotifiableimplementiert. IhrgetChannel()sollte'SMS'zurückgeben, und ihresend()Methode sollteSending SMS: [message]ausgeben, dannlogSent()mit dem Kanal und der Nachricht aufrufen und anschließendlogTimestamp()aufrufen.
EmailNotificationund rufen Siesend('Hello World')auf, geben Sie eine Leerzeile aus, erstellen Sie eineSMSNotificationund rufen Siesend('Quick update')auf.- Eine
Beachten Sie, wie das Interface jede Klasse dazu zwingt, ihre eigenen Implementierungen für send() und getChannel() zu schreiben (unterschiedliches Verhalten), während das Mixin die Logging-Methoden bereitstellt, die für beide identisch funktionieren (gemeinsames Verhalten).
Erwartete Ausgabe:
Sending email: Hello World
[LOG] Sent via Email: Hello World
[LOG] Timestamp recorded
Sending SMS: Quick update
[LOG] Sent via SMS: Quick update
[LOG] Timestamp recordedSpickzettel
Schnittstellen definieren einen Vertrag (Methodensignaturen), während Mixins die Implementierung (tatsächlichen Code) bereitstellen.
Wenn Sie eine Schnittstelle implementieren, müssen Sie den gesamten Code selbst schreiben:
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // Dies schreiben Sie selbst
}
}Wenn Sie ein Mixin verwenden, erhalten Sie die Implementierung kostenlos dazu:
mixin Printable {
void printInfo() {
print('Printing...'); // Bereits für Sie geschrieben
}
}
class Report with Printable {} // Erhält printInfo() automatischWann man was verwendet:
- Verwenden Sie eine Schnittstelle, wenn verschiedene Klassen dieselben Methodensignaturen, aber unterschiedliche Implementierungen benötigen
- Verwenden Sie ein Mixin, wenn Klassen genau dasselbe Verhalten teilen sollen
| Merkmal | Schnittstelle | Mixin |
|---|---|---|
| Bietet | Nur Vertrag | Vertrag + Code |
| Implementierung | Sie schreiben sie | Inbegriffen |
| Schlüsselwort | implements | with |
| Bestens geeignet für | Unterschiedliche Verhaltensweisen, gleiche API | Gemeinsames Verhalten |
Sie können beide Ansätze kombinieren. Setzen Sie beim Kombinieren with immer vor implements:
class MyClass with SomeMixin implements SomeInterface {
// with steht vor implements
}Probier es selbst
import 'notifiable.dart';
import 'logging.dart';
// TODO: Erstelle die Klasse EmailNotification, die:
// - das Logging Mixin verwendet (with)
// - Notifiable implementiert (implements)
// - getChannel() gibt 'Email' zurück
// - send() gibt 'Sending email: [message]' aus, ruft dann logSent() und danach logTimestamp() auf
// TODO: Erstelle die Klasse SMSNotification, die:
// - das Logging Mixin verwendet (with)
// - Notifiable implementiert (implements)
// - getChannel() gibt 'SMS' zurück
// - send() gibt 'Sending SMS: [message]' aus, ruft dann logSent() und danach logTimestamp() auf
void main() {
// TODO: Erstelle eine EmailNotification und rufe send('Hello World') auf
// TODO: Gib eine Leerzeile aus
// TODO: Erstelle eine SMSNotification und rufe send('Quick update') 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