Mixins

O que são Mixins?

Um Mixin é uma forma de reutilizar código. Ele possibilita reaproveitar lógica em classes que não compartilham a mesma cadeia de herança, com o objetivo de implementar membros em várias classes em lote. Se assemelha um pouco à herança múltipla, mas sua finalidade é adicionar funcionalidades modulares a uma classe.

Utilizar um Mixin

Use a palavra-chave with, seguida de um ou mais nomes 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;
  }
}
Code language: Dart (dart)

Declarar um Mixin

Declare utilizando a palavra reservada mixin.

Observação: mixin class (Dart 3.0+) funciona tanto como uma classe comum quanto como um Mixin.

Regras obrigatórias

  1. mixin / mixin class não aceita o comando extends
  2. Não é possível declarar construtores gerativos
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 language: Dart (dart)

Resultado da execução

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

Mixin que precisa acessar membros externos

Métodos ou propriedades que o próprio Mixin não implementa

Muitas vezes um Mixin precisa chamar membros que ele não implementa. Veja quatro abordagens padrão abaixo.

Definir membros abstratos dentro do Mixin

O Mixin declara métodos abstratos, e toda classe que usar esse Mixin deve implementá-los.

mixin Musician {
  // Método abstrato que deve ser implementado por quem usar
  void playInstrument(String instrumentName);

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

// A classe usuária precisa sobrescrever 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();
}
Code language: Dart (dart)
Plays the Piano beautifully
Plays the Flute beautifully

Acessar o estado da classe hospedeira via getter abstrato

O Mixin define um getter abstrato para ler campos da classe hospedeira, permitindo acessar estados entre classes diferentes.

/// Pode ser misturado em qualquer classe com propriedade name; fornece automaticamente == e hashCode baseados no nome
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 language: JavaScript (javascript)

Mixin implementando interface com implements

Usando implements, o Mixin define restrições de interface, obrigando a classe que o incorpora a implementar todos os métodos da 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();
}
Code language: JavaScript (javascript)
Don't bother, being out of tune is punk rock.
Strums guitar majestically.

Cláusula on: limitar quais classes podem usar o Mixin

Função do on: apenas classes derivadas do tipo informado conseguem incorporar esse Mixin.

Além disso, dentro do Mixin é possível acessar membros da classe base e chamar super.xxx().

Só use on se precisar invocar métodos da classe pai com super dentro do Mixin!

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

// Só pode ser usado por subclasses de Musician
mixin MusicalPerformer on Musician {
  void performerMethod() {
    print('Performing music!');
    super.musicianMethod(); // Chama método da classe base definida no on
  }
}

// SingerDancer herda de Musician, então pode usar MusicalPerformer
class SingerDancer extends Musician with MusicalPerformer {}

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

A classe SingerDancer incorpora o MusicalPerformer e, por isso, consegue usar o método performerMethod

Funciona assim: uma classe importa os métodos de um Mixin para usá-los diretamente.

⚠️ Exemplo incorreto:

Se uma classe não herdar de Musician e tentar usar with MusicalPerformer, ocorrerá erro de compilação.


Diferença entre class / mixin / mixin class

  1. mixin: só pode ser usado com with, não aceita herança via extends
  2. class: aceita extends, não pode ser usado diretamente como mixin
  3. mixin class:
    • funciona como Mixin usando with
    • também pode ser classe base comum com extends

As restrições também valem:

  • mixin / mixin class não aceitam extends nem with
  • class / mixin class não aceitam on
mixin class Musician {
  void play() {
    print("Musician is playing");
  }
}

// Usado como Mixin
class Novice1 with Musician {}

// Usado como classe base comum
class Novice2 extends Musician {}

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

Resultado da execução

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

Mixins

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *