Mixins

¿Qué son los Mixins?

Un Mixin es una forma de reutilizar código. Permite compartir lógica entre clases que no forman parte de la misma jerarquía de herencia, con el propósito de dotar a múltiples clases de implementaciones de miembros. Se asemeja un poco a la herencia múltiple, pero su objetivo es añadir funcionalidades modulares a una clase.

Uso de Mixin

Se emplea la palabra clave with, seguida de uno o varios nombres de Mixin.

class Musician extends Performer with Musical {
  // Código
}

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
Lenguaje del código: Dart (dart)

Definir un Mixin

Se declara mediante la palabra reservada mixin.

Anotación: mixin class (Dart 3.0+) se puede usar tanto como clase normal como Mixin.

Restricciones obligatorias

  1. mixin / mixin class no admite el uso de extends
  2. No se pueden declarar constructores generativos
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
}
Lenguaje del código: Dart (dart)

Resultado de ejecución

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playingLenguaje del código: CSS (css)

Mixin que requiere acceder a miembros externos

Métodos o propiedades que el propio Mixin no define

Frecuentemente, un Mixin necesita invocar miembros cuya implementación no posee. A continuación se exponen cuatro soluciones estándar.

Declarar miembros abstractos dentro del Mixin

El Mixin declara métodos abstractos, y toda clase que utilice este Mixin debe implementarlos.

mixin Musician {
  // Método abstracto que debe implementar quien lo use
  void playInstrument(String instrumentName);

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

// El usuario debe sobrescribir e implementar
class Virtuoso with Musician {
  @override
  void playInstrument(String instrumentName) {
    print('Plays the $instrumentName beautifully');
  }
}

void main() {
  var artist = Virtuoso();
  artist.playPiano();
  artist.playFlute();
}
Lenguaje del código: Dart (dart)
Plays the Piano beautifully
Plays the Flute beautifully

Acceder al estado de la clase huésped mediante getters abstractos

El Mixin define un getter abstracto para leer campos de la clase huésped y acceder al estado entre distintas clases.

/// Se puede mezclar en cualquier clase que cuente con propiedad name; proporciona automáticamente == y hashCode basados en 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
}
Lenguaje del código: JavaScript (javascript)

Mixin que implementa interfaces con implements

Mediante implements, el Mixin establece restricciones de interfaz, obligando a la clase mezclada a definir todos los métodos de la interfaz.

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();
}
Lenguaje del código: JavaScript (javascript)
Don't bother, being out of tune is punk rock.
Strums guitar majestically.

Cláusula on: limitar qué clases pueden emplear el Mixin

Función de on: solo las clases derivadas del tipo especificado podrán incorporar este Mixin.

Además, dentro del Mixin se pueden invocar miembros de la clase padre y llamar a super.xxx().

¡Usa on únicamente si necesitas llamar métodos del padre con super desde el Mixin!

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

// Solo se puede mezclar en subclases de Musician
mixin MusicalPerformer on Musician {
  void performerMethod() {
    print('Performing music!');
    super.musicianMethod(); // Permite llamar métodos de la clase padre definida en on
  }
}

// SingerDancer hereda de Musician, por lo tanto puede usar MusicalPerformer
class SingerDancer extends Musician with MusicalPerformer {}

void main() {
  SingerDancer().performerMethod();
}
Lenguaje del código: Dart (dart)
Performing music!
Playing music!

SingerDancer incorpora MusicalPerformer, por lo que puede usar su método performerMethod

La mezcla funciona así: una clase puede tomar los métodos de otro Mixin para usarlos directamente.

⚠️ Ejemplo incorrecto:

Si una clase no hereda de Musician y usa directamente with MusicalPerformer, se producirá un error de compilación.


Diferencias entre class / mixin / mixin class

  1. mixin: solo se usa mediante with, no se puede heredar con extends
  2. class: admite extends, no se puede emplear directamente como mixin
  3. mixin class:
    • se puede usar como Mixin con with
    • también puede funcionar como clase padre normal mediante extends

También se aplican las restricciones:

  • mixin / mixin class no permiten extends ni with
  • class / mixin class no admiten on
mixin class Musician {
  void play() {
    print("Musician is playing");
  }
}

// Uso como Mixin
class Novice1 with Musician {}

// Uso como clase padre común
class Novice2 extends Musician {}

void main() {
  Novice1().play();
  Novice2().play();
}
Lenguaje del código: Dart (dart)

Resultado de ejecución

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playing
Lenguaje del código: CSS (css)

Mixins

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *