Mixin vs. Schnittstelle
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 du ein Interface implementierst, versprichst du, bestimmte Methoden bereitzustellen, aber du schreibst den gesamten Code selbst. Wenn du ein Mixin verwendest, erhältst du den eigentlichen 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 solltest du also welches verwenden? Verwende ein Interface, wenn verschiedene Klassen dieselben Methodensignaturen, aber unterschiedliche Implementierungen benötigen. Verwende ein Mixin, wenn Klassen dasselbe Verhalten teilen sollen.
| Funktion | Schnittstelle | Mixin |
|---|---|---|
| Bietet | Nur Vertrag | Vertrag + Code |
| Implementierung | Du schreibst sie | Enthalten |
| Schlüsselwort | implements | with |
| Am besten geeignet 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
EinfachErstellen wir ein Benachrichtigungssystem, das zeigt, wann Schnittstellen und wann Mixins verwendet werden sollten. Du erstellst eine Schnittstelle für Benachrichtigungen, die unterschiedliche Implementierungen benötigen, und ein Mixin für gemeinsames Protokollierungsverhalten, das bei allen Benachrichtigungstypen identisch sein sollte.
Du organisierst deinen Code in drei Dateien:
notifiable.dart: Definiere eine abstrakte KlasseNotifiable, die als Schnittstelle dient. Sie sollte zwei abstrakte Methoden deklarieren:send(String message)undgetChannel(), die einenStringzurückgibt. Verschiedene Benachrichtigungstypen implementieren diese Methoden je nach ihrem spezifischen Kanal unterschiedlich.logging.dart: Erstelle einLogging-Mixin, das gemeinsames Verhalten bereitstellt, das alle Benachrichtigungen haben sollten. Füge eine MethodelogSent(String channel, String message)hinzu, die[LOG] Sent via [channel]: [message]ausgibt, sowie eine MethodelogTimestamp(), die[LOG] Timestamp recordedausgibt. Dieses Verhalten ist für alle Benachrichtigungstypen identisch und eignet sich daher perfekt für ein Mixin.main.dart: Importiere beide Dateien und erstelle zwei Benachrichtigungsklassen, die die Schnittstelle mit dem Mixin kombinieren:- Eine Klasse
EmailNotification, die dasLogging-Mixin verwendet undNotifiableimplementiert. IhrgetChannel()sollte'Email'zurückgeben, und ihresend()-Methode sollteSending email: [message]ausgeben, anschließendlogSent()mit dem Kanal und der Nachricht aufrufen und danachlogTimestamp()aufrufen. - Eine Klasse
SMSNotification, die dasLogging-Mixin verwendet undNotifiableimplementiert. IhrgetChannel()sollte'SMS'zurückgeben, und ihresend()-Methode sollteSending SMS: [message]ausgeben, anschließendlogSent()mit dem Kanal und der Nachricht aufrufen und danachlogTimestamp()aufrufen.
EmailNotificationund rufesend('Hello World')auf, gib eine leere Zeile aus, erstelle einSMSNotificationund rufesend('Quick update')auf.- Eine Klasse
Beachte, wie die Schnittstelle jede Klasse dazu zwingt, ihre eigenen Implementierungen von send() und getChannel() zu schreiben (unterschiedliches Verhalten), während das Mixin die Protokollierungsmethoden 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 recordedProbier 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 Schlüsselwort thisMethodenInstanzvariablenGrundlagen von KonstruktorenRückblick – Einfacher Taschenrechner4Null-Sicherheit
Einführung in die Null-SicherheitNullable vs. Non-NullableDie Operatoren ? und !Late-Schlüsselwort und Null-SicherheitNull-bewusste OperatorenNull-Sicherheit in KlassenRückblick – Benutzers профilsystem7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrere InterfacesZusammenfassung – Formrechner10Collections & Generics
Übersicht über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische EinschränkungenIterable & IteratorRückblick – Generischer Speicher13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungErweiterungsmethodenAufrufbare KlassenVersiegelte Klassen (Dart 3)Records (Dart 3)Muster & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
StandardkonstruktorBenannte KonstruktorenInitialisierungslistenKonstantenkonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenRückblick – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _-Präfix-KonventionBibliotheksweite SichtbarkeitTiefergehende Getter & SetterInformationsverbergungRückblick – Studierendendaten8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas Schlüsselwort „on“ in MixinsMixin vs. VererbungMixin vs. SchnittstelleRückblick – Tiersystem11Spezielle Methoden
toString()-ÜberschreibunghashCode- & ==-ÜberschreibungComparable-Schnittstellecall()-MethodenoSuchMethod-ÜberschreibungWiederholung – Benutzerdefinierte Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFabrikmusterBeobachtermusterStrategiemuster17Abschließende Herausforderungen
E-Learning-PlattformBankingsystemSpielcharaktersystemFahrzeugvermietung3Klassen-Eigenschaften
Instanz- vs. statische MemberFinal- und Const-FelderLate-VariablenStatische Methoden und FelderGetter und SetterRückblick – Bankkontoverwaltung6Vererbung
Grundlegende VererbungDas Schlüsselwort superMethodenüberschreibungDie Annotation @overrideDas Schlüsselwort final für KlassenKonstruktoren und VererbungZusammenfassung – Mitarbeiterhierarchie9Polymorphismus
Grundlagen des PolymorphismusPolymorphismus über InterfacesLaufzeit-TypüberprüfungDie Operatoren is & asCovariant-SchlüsselwortZusammenfassung – ZahlungsprozessorÜbe selbstständig: Online-Dart-Compiler