Implizite Interfaces
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 46 von 110.
Da jede Dart-Klasse automatisch ein Interface definiert, können Sie jede Klasse als Interface verwenden – sogar konkrete Klassen mit vollständigen Implementierungen. Dies bezeichnen wir als ein implizites Interface.
Betrachten Sie eine Klasse mit Eigenschaften und Methoden:
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}Wenn eine andere Klasse Logger implementiert, muss sie ihre eigene Implementierung für alles bereitstellen – einschließlich der Eigenschaft prefix:
class FileLogger implements Logger {
@override
String prefix = '[FILE]';
@override
void log(String message) {
print('$prefix Writing to file: $message');
}
@override
void clear() {
print('Deleting log file...');
}
}Das ist die entscheidende Erkenntnis: Wenn Sie implements verwenden, sagen Sie: „meine Klasse wird dieselbe Form haben“ – die gleichen Methoden und Eigenschaften. Sie erben keinen Code, keine Konstruktoren oder Standardwerte. Sie versprechen lediglich, der Struktur des Interfaces zu entsprechen.
Dies macht implizite Schnittstellen leistungsstark für die Erstellung austauschbarer Komponenten. Code, der einen Logger erwartet, kann mit jeder Klasse arbeiten, die dessen Schnittstelle implementiert, unabhängig davon, wie diese Implementierung intern funktioniert.
Aufgabe
EinfachLassen Sie uns ein Benachrichtigungssystem erstellen, das zeigt, wie konkrete Klassen als implizite Schnittstellen dienen können. Sie werden eine Basis-Benachrichtigungsklasse erstellen und diese dann auf völlig unterschiedliche Weise implementieren, um zu zeigen, dass implements von Ihnen verlangt, alle Member selbst bereitzustellen.
Sie werden Ihren Code in zwei Dateien organisieren:
notification.dart: Definieren Sie hier Ihre Benachrichtigungsklassen:- Eine
Notification-Klasse mit einerString channel-Eigenschaft, die auf'default'gesetzt ist, einer Methodesend(String message), die[channel]: [message]ausgibt, und einer MethodegetStatus(), die den String'Notification ready'zurückgibt. - Eine
EmailNotification-Klasse, dieNotificationimplementiert. Da Sieimplementsverwenden, müssen Sie Ihre eigenechannel-Eigenschaft bereitstellen (setzen Sie diese auf'email'), Ihre eigenesend()-Methode, dieSending email: [message]ausgibt, und Ihre eigenegetStatus()-Methode, die'Email service active'zurückgibt. - Eine
PushNotification-Klasse, dieNotificationimplementiert. Setzen Sie derenchannelauf'push', lassen Siesend()den TextPush alert: [message]ausgeben und lassen SiegetStatus()den Wert'Push service connected'zurückgeben.
- Eine
main.dart: Importieren Sie Ihre Benachrichtigungsdatei und demonstrieren Sie, wie verschiedene Klassen dieselbe implizite Schnittstelle mit völlig unterschiedlichem Verhalten implementieren können:- Erstellen Sie eine
EmailNotificationund einePushNotification. - Geben Sie für jede Benachrichtigung ihren Status mit
getStatus()aus und rufen Sie dannsend()mit der Nachricht'Hello World'auf. - Geben Sie eine Leerzeile zwischen den beiden Benachrichtigungen aus.
- Erstellen Sie eine
Denken Sie daran: Wenn Sie implements verwenden, erben Sie keinen Code von Notification. Sie versprechen lediglich, dass Ihre Klasse dieselbe Form haben wird – dieselben Eigenschaften und Methoden. Jede implementierende Klasse stellt ihre eigene vollständige Implementierung bereit.
Erwartete Ausgabe:
Email service active
Sending email: Hello World
Push service connected
Push alert: Hello WorldSpickzettel
In Dart definiert jede Klasse automatisch ein implizites Interface. Das bedeutet, dass Sie jede Klasse als Interface verwenden können, sogar konkrete Klassen mit vollständigen Implementierungen.
Wenn eine Klasse implements verwendet, muss sie ihre eigene Implementierung für alle Eigenschaften und Methoden des Interfaces bereitstellen. Sie erben keinen Code, keine Konstruktoren oder Standardwerte – Sie versprechen lediglich, der Struktur des Interfaces zu entsprechen.
Beispiel einer konkreten Klasse:
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}Implementierung des impliziten Interfaces:
class FileLogger implements Logger {
@override
String prefix = '[FILE]';
@override
void log(String message) {
print('$prefix Writing to file: $message');
}
@override
void clear() {
print('Deleting log file...');
}
}Dies macht implizite Interfaces zu einem mächtigen Werkzeug für die Erstellung austauschbarer Komponenten. Code, der einen Logger erwartet, kann mit jeder Klasse arbeiten, die dessen Interface implementiert, unabhängig von der internen Implementierung.
Probier es selbst
import 'notification.dart';
void main() {
// TODO: Erstellen Sie eine EmailNotification-Instanz
// TODO: Geben Sie den Status der E-Mail-Benachrichtigung mit getStatus() aus
// TODO: Senden Sie eine Nachricht 'Hello World' mit der E-Mail-Benachrichtigung
// TODO: Geben Sie eine Leerzeile aus
// TODO: Erstellen Sie eine PushNotification-Instanz
// TODO: Geben Sie den Status der Push-Benachrichtigung mit getStatus() aus
// TODO: Senden Sie eine Nachricht 'Hello World' mit der Push-Benachrichtigung
}
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