Mixins

Qu’est-ce qu’un Mixin

Le Mixin est une méthode de réutilisation du code. Il permet de partager du code entre des hiérarchies de classes sans lien d’héritage direct. Son objectif est d’ajouter collectivement des implémentations de membres à plusieurs classes. Il ressemble un peu à l’héritage multiple, mais son but principal est d’apporter des fonctionnalités modulaires à une classe.

Utiliser un Mixin

On utilise le mot-clé with, suivi d’un ou plusieurs noms de Mixin.

class Musician extends Performer with Musical {
  // code
}

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
Langage du code : Dart (dart)

Définir un Mixin

On le déclare avec le mot-clé mixin.

Complément : mixin class (Dart 3.0+) peut fonctionner à la fois comme une classe classique et comme un Mixin.

Contraintes obligatoires

  1. Dans un mixin / mixin class, il est interdit d’écrire extends
  2. Impossible de déclarer des constructeurs générateurs (generative constructor)
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
}
Langage du code : Dart (dart)

Résultat d’exécution

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playingLangage du code : CSS (css)

Mixin ayant besoin d’appeler des membres externes

Méthodes / propriétés non définies au sein du Mixin

Un Mixin doit souvent appeler des membres qu’il n’implémente pas lui-même. Voici quatre solutions standard.

Définir des membres abstraits dans le Mixin

Le Mixin déclare une méthode abstraite. Toute classe utilisant ce Mixin doit implémenter cette méthode abstraite.

mixin Musician {
  // Méthode abstraite, à implémenter par l’utilisateur
  void playInstrument(String instrumentName);

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

// L’utilisateur doit redéfinir et implémenter
class Virtuoso with Musician {
  @override
  void playInstrument(String instrumentName) {
    print('Plays the $instrumentName beautifully');
  }
}

void main() {
  var artist = Virtuoso();
  artist.playPiano();
  artist.playFlute();
}
Langage du code : Dart (dart)
Plays the Piano beautifully
Plays the Flute beautifully

Accéder à l’état de la classe hôte via un Getter abstrait

Le Mixin définit un getter abstrait pour lire les champs de la classe hôte et accéder à l’état d’une autre classe.

/// Peut être mélangé à toute classe disposant d’une propriété name, fournit automatiquement == et hashCode basés sur name
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
}
Langage du code : JavaScript (javascript)

Mixin qui implémente une interface avec implements

Avec implements, le Mixin contraint une interface et force la classe à intégrer toutes les méthodes de cette interface.

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();
}
Langage du code : JavaScript (javascript)
Don't bother, being out of tune is punk rock.
Strums guitar majestically.

Clause on : restreindre le Mixin aux sous-classes d’un type précis

Rôle de on : seules les classes héritant du type spécifié peuvent utiliser ce Mixin.

De plus, au sein du Mixin, on peut appeler les membres de la classe parente et utiliser super.xxx().

N’utilisez on que si vous devez appeler des méthodes parentes avec super dans le Mixin !

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

// Ne peut être mélangé qu’aux sous-classes de Musician
mixin MusicalPerformer on Musician {
  void performerMethod() {
    print('Performing music!');
    super.musicianMethod(); // Appel possible des méthodes parentes limitées par on
  }
}

// SingerDancer hérite de Musician, il peut donc utiliser MusicalPerformer
class SingerDancer extends Musician with MusicalPerformer {}

void main() {
  SingerDancer().performerMethod();
}
Langage du code : Dart (dart)
Performing music!
Playing music!

SingerDancer utilise MusicalPerformer, il peut donc accéder à la méthode performerMethod qui s’y trouve.

Concrètement, le mélange permet à une classe de récupérer les méthodes d’un Mixin pour les utiliser comme si elles lui appartenaient.

⚠️ Exemple erroné :

Si une classe n’hérite pas de Musician, écrire directement with MusicalPerformer provoque une erreur de compilation.


Différences entre class /mixin/mixin class

  1. mixin : utilisable uniquement via with, impossible à hériter par extends
  2. class : peut être héritée avec extends, ne peut pas servir directement de mixin
  3. mixin class:
    • peut servir de Mixin via with
    • peut aussi être héritée normalement avec extends

Mêmes contraintes applicables :

  • Dans mixin / mixin class, pas d’écriture de extends, with
  • Dans class / mixin class, pas d’écriture de on
mixin class Musician {
  void play() {
    print("Musician is playing");
  }
}

// Utilisé comme Mixin
class Novice1 with Musician {}

// Utilisé comme classe parente classique
class Novice2 extends Musician {}

void main() {
  Novice1().play();
  Novice2().play();
}
Langage du code : Dart (dart)

Résultat d’exécution

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playing
Langage du code : CSS (css)

Mixins

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *