Mixins

Was sind Mixins

Ein Mixin ist ein Verfahren zur Code-Wiederverwendung. Es erlaubt Code über unabhängige Klassenvererbungsbäume hinweg gemeinsam zu nutzen. Das Ziel ist, mehreren Klassen auf einen Schlag Mitgliederimplementierungen zur Verfügung zu stellen. Es erinnert an Mehrfachvererbung, aber der eigentliche Zweck ist, einer Klasse verschiedene modulare Funktionen hinzuzufügen.

Mixin verwenden

Nutze das Schlüsselwort with, gefolgt von einem oder mehreren Mixin-Namen.

class Musician extends Performer with Musical {
  // Code
}

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
Code-Sprache: Dart (dart)

Mixin definieren

Deklariere mit dem Schlüsselwort mixin.

Hinweis: mixin class (ab Dart 3.0+) kann sowohl als normale Klasse als auch als Mixin eingesetzt werden.

Zwingende Einschränkungen

  1. In mixin / mixin class darf kein extends geschrieben werden
  2. Es dürfen keine generativen Konstruktoren deklariert werden
mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

class Musician with Musical {}

void main() {
  var m = Musician();
  m.canConduct = true;
  m.entertainMe(); // Waving hands
}
Code-Sprache: Dart (dart)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playingCode-Sprache: CSS (css)

Mixin muss externe Mitglieder aufrufen

Methoden / Eigenschaften, die das Mixin selbst nicht definiert

Ein Mixin benötigt oft Zugriff auf Mitglieder, die es nicht selbst implementiert. Hier werden vier gängige Standardlösungen vorgestellt.

Abstrakte Mitglieder im Mixin festlegen

Das Mixin deklariert eine abstrakte Methode. Jede Klasse, die dieses Mixin nutzt, muss diese abstrakte Methode implementieren.

mixin Musician {
  // Abstrakte Methode, muss vom Nutzer implementiert werden
  void playInstrument(String instrumentName);

  void playPiano() {
    playInstrument('Piano');
  }
  void playFlute() {
    playInstrument('Flute');
  }
}

// Nutzer muss die Methode überschreiben und umsetzen
class Virtuoso with Musician {
  @override
  void playInstrument(String instrumentName) {
    print('Plays the $instrumentName beautifully');
  }
}

void main() {
  var artist = Virtuoso();
  artist.playPiano();
  artist.playFlute();
}
Code-Sprache: Dart (dart)
Plays the Piano beautifully
Plays the Flute beautifully

Zugriff auf Zustand der Wirtsklasse per abstraktem Getter

Das Mixin definiert einen abstrakten Getter, um Felder der Wirtsklasse auszulesen und klassenübergreifend auf Zustände zuzugreifen.

/// Kann in jede Klasse mit name-Eigenschaft eingebunden werden, stellt automatisch == und hashCode auf Basis von name bereit
mixin NameIdentity {
  String get name;

  @override
  int get hashCode => name.hashCode;

  @override
  bool operator ==(Object other) =>
      other is NameIdentity && name == other.name;
}

class Person with NameIdentity {
  final String name;
  Person(this.name);
}

void main() {
  var p1 = Person("Alice");
  var p2 = Person("Alice");
  var p3 = Person("Bob");
  print(p1 == p2); // true
  print(p1 == p3); // false
}
Code-Sprache: JavaScript (javascript)

Mixin implementiert Schnittstelle mit implements

Mit implements beschränkt das Mixin eine Schnittstelle und zwingt die einbindende Klasse, alle Methoden der Schnittstelle zu vervollständigen.

abstract interface class Tuner {
  void tuneInstrument();
}

mixin Guitarist implements Tuner {
  void playSong() {
    tuneInstrument();
    print('Strums guitar majestically.');
  }
}

class PunkRocker with Guitarist {
  @override
  void tuneInstrument() {
    print("Don't bother, being out of tune is punk rock.");
  }
}

void main() {
  PunkRocker().playSong();
}
Code-Sprache: JavaScript (javascript)
Don't bother, being out of tune is punk rock.
Strums guitar majestically.

on-Klausel: Mixin nur für Unterklassen eines bestimmten Typs zulassen

Funktion von on: Nur Klassen, die von dem festgelegten Typ erben, dürfen dieses Mixin einbinden.

Zusätzlich kann das Mixin innerhalb auf Mitglieder der Elternklasse zugreifen und super.xxx() aufrufen.

Nutze on nur, wenn du im Mixin mit super Elternmethoden aufrufen möchtest!

class Musician {
  void musicianMethod() {
    print('Playing music!');
  }
}

// Darf nur in Unterklassen von Musician eingebunden werden
mixin MusicalPerformer on Musician {
  void performerMethod() {
    print('Performing music!');
    super.musicianMethod(); // Aufruf der durch on begrenzten Elternmethode möglich
  }
}

// SingerDancer erbt von Musician, also kann MusicalPerformer eingebunden werden
class SingerDancer extends Musician with MusicalPerformer {}

void main() {
  SingerDancer().performerMethod();
}
Code-Sprache: Dart (dart)
Performing music!
Playing music!

SingerDancer bindet MusicalPerformer ein und kann daher dessen Methode performerMethod nutzen.

Ein Mixin funktioniert grob gesagt so: Eine Klasse holt sich alle Methoden aus einem anderen Mixin und kann diese anschließend verwenden.

⚠️ Falsches Beispiel:

Wenn eine Klasse nicht von Musician erbt und direkt with MusicalPerformer schreibt, kommt es zu einem Kompilierfehler.


Unterschiede zwischen class /mixin/mixin class

  1. mixin: Nur mit with nutzbar, keine Vererbung via extends
  2. class: Kann mit extends vererbt werden, nicht direkt als Mixin verwendbar
  3. mixin class:
    • kann mit with als Mixin verwendet werden
    • kann zusätzlich mit extends als normale Elternklasse dienen

Gleiche Einschränkungen gelten:

  • In mixin / mixin class kein extends, with schreiben
  • In class / mixin class kein on schreiben
mixin class Musician {
  void play() {
    print("Musician is playing");
  }
}

// Als Mixin verwenden
class Novice1 with Musician {}

// Als normale Elternklasse vererben
class Novice2 extends Musician {}

void main() {
  Novice1().play();
  Novice2().play();
}
Code-Sprache: Dart (dart)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playing
Code-Sprache: CSS (css)

Mixins

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert