Menu
Coddy logo textTech

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() automatisch

Wann 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.

MerkmalInterfaceMixin
BietetNur VertragVertrag + Code
ImplementierungSelbst geschriebenEnthalten
Schlüsselwortimplementswith
Ideal fürUnterschiedliche Verhaltensweisen, gleiche APIGemeinsames 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
}
challenge icon

Aufgabe

Einfach

Lassen 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 Klasse Notifiable, die als Interface fungiert. Sie sollte zwei abstrakte Methoden deklarieren: send(String message) und getChannel(), die einen String zurückgibt. Verschiedene Benachrichtigungstypen werden diese Methoden basierend auf ihrem spezifischen Kanal unterschiedlich implementieren.
  • logging.dart: Erstellen Sie ein Logging Mixin, das gemeinsames Verhalten bereitstellt, das alle Benachrichtigungen haben sollten. Fügen Sie eine Methode logSent(String channel, String message) hinzu, die [LOG] Sent via [channel]: [message] ausgibt, und eine Methode logTimestamp(), die [LOG] Timestamp recorded ausgibt. 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 EmailNotification Klasse, die das Logging Mixin verwendet und Notifiable implementiert. Ihr getChannel() sollte 'Email' zurückgeben, und ihre send() Methode sollte Sending email: [message] ausgeben, dann logSent() mit dem Kanal und der Nachricht aufrufen und anschließend logTimestamp() aufrufen.
    • Eine SMSNotification Klasse, die das Logging Mixin verwendet und Notifiable implementiert. Ihr getChannel() sollte 'SMS' zurückgeben, und ihre send() Methode sollte Sending SMS: [message] ausgeben, dann logSent() mit dem Kanal und der Nachricht aufrufen und anschließend logTimestamp() aufrufen.
    Erstellen Sie dann eine EmailNotification und rufen Sie send('Hello World') auf, geben Sie eine Leerzeile aus, erstellen Sie eine SMSNotification und rufen Sie send('Quick update') auf.

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 recorded

Spickzettel

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() automatisch

Wann 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
MerkmalSchnittstelleMixin
BietetNur VertragVertrag + Code
ImplementierungSie schreiben sieInbegriffen
Schlüsselwortimplementswith
Bestens geeignet fürUnterschiedliche Verhaltensweisen, gleiche APIGemeinsames 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
}
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