Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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 einer String channel-Eigenschaft, die auf 'default' gesetzt ist, einer Methode send(String message), die [channel]: [message] ausgibt, und einer Methode getStatus(), die den String 'Notification ready' zurückgibt.
    • Eine EmailNotification-Klasse, die Notification implementiert. Da Sie implements verwenden, müssen Sie Ihre eigene channel-Eigenschaft bereitstellen (setzen Sie diese auf 'email'), Ihre eigene send()-Methode, die Sending email: [message] ausgibt, und Ihre eigene getStatus()-Methode, die 'Email service active' zurückgibt.
    • Eine PushNotification-Klasse, die Notification implementiert. Setzen Sie deren channel auf 'push', lassen Sie send() den Text Push alert: [message] ausgeben und lassen Sie getStatus() den Wert 'Push service connected' zurückgeben.
  • 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 EmailNotification und eine PushNotification.
    • Geben Sie für jede Benachrichtigung ihren Status mit getStatus() aus und rufen Sie dann send() mit der Nachricht 'Hello World' auf.
    • Geben Sie eine Leerzeile zwischen den beiden Benachrichtigungen aus.

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 World

Spickzettel

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
  
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung