Что такое Mixin?
Mixin — это способ повторного использования кода. Он позволяет переиспользовать логику в классах, которые не входят в одну иерархию наследования, и массово добавлять реализацию членов в классы. Похоже на множественное наследование, но главная цель — добавить модульные функции в существующий класс.
Использование Mixin
Используйте ключевое слово with, после которого указывается одно или несколько имен Mixin.
class Musician extends Performer with Musical {
// Код
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
Code language: Dart (dart)
Определение Mixin
Объявляется с помощью ключевого слова mixin.
Примечание:
mixin class(Dart 3.0+) можно использовать как обычный класс и как Mixin.
Обязательные ограничения
mixin/mixin classне поддерживает операторextends- Нельзя объявлять генеративные конструкторы
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)
Результат выполнения
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playingCode language: CSS (css)
Mixin, обращающийся к внешним членам
Методы или свойства, которые сам Mixin не реализует
Часто Mixin вызывает члены, реализацию которых не содержит. Ниже представлены четыре стандартных решения.
Объявление абстрактных членов внутри Mixin
Mixin объявляет абстрактные методы, и любой класс, использующий этот Mixin, обязан реализовать их.
mixin Musician {
// Абстрактный метод, который должен реализовать пользователь
void playInstrument(String instrumentName);
void playPiano() {
playInstrument('Piano');
}
void playFlute() {
playInstrument('Flute');
}
}
// Пользователь должен переопределить метод
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
Доступ к состоянию родительского класса через абстрактный геттер
Mixin определяет абстрактный геттер для чтения полей класса-хозяина, обеспечивая доступ к состоянию между разными классами.
/// Можно подмешать в любой класс с полем name, автоматически добавляет реализацию == и hashCode по имени
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 с реализацией интерфейса через implements
С помощью implements Mixin задает ограничения интерфейса, заставляя класс с подмешанным Mixin реализовать все методы интерфейса.
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.
Оператор on: ограничение классов, способных использовать Mixin
Назначение on: только классы, наследующие указанный тип, могут использовать данный Mixin.
Кроме того, внутри Mixin можно вызывать члены родительского класса и обращаться к super.xxx().
Используйте
onтолько тогда, когда внутри Mixin требуется вызывать методы предка черезsuper!
class Musician {
void musicianMethod() {
print('Playing music!');
}
}
// Подмешивается только в подклассы Musician
mixin MusicalPerformer on Musician {
void performerMethod() {
print('Performing music!');
super.musicianMethod(); // Вызов метода родительского класса из ограничения on
}
}
// SingerDancer наследует Musician, поэтому может использовать MusicalPerformer
class SingerDancer extends Musician with MusicalPerformer {}
void main() {
SingerDancer().performerMethod();
}
Code language: Dart (dart)
Performing music!
Playing music!
Класс SingerDancer подмешал MusicalPerformer, поэтому может использовать метод performerMethod из него
Принцип подмешивания простой: класс получает все методы из Mixin и может использовать их напрямую.
⚠️ Неверный пример:
Если класс не наследует
Musicianи пытается использоватьwith MusicalPerformer, возникнет ошибка компиляции.
Разница между class / mixin / mixin class
mixin: используется только черезwith, наследование черезextendsзапрещеноclass: поддерживаетextends, нельзя напрямую использовать как mixinmixin class:- можно использовать как Mixin с оператором
with - можно использовать как обычный родительский класс через
extends
- можно использовать как Mixin с оператором
Ограничения сохраняются:
- mixin / mixin class не допускают extends и with
- class / mixin class не поддерживают on
mixin class Musician { void play() { print("Musician is playing"); } } // Использование как Mixin class Novice1 with Musician {} // Использование как обычный родительский класс class Novice2 extends Musician {} void main() { Novice1().play(); Novice2().play(); }Code language: Dart (dart)Результат выполнения
D:\dartdemo\firstdart>dart run Building package executable... Built firstdart:firstdart. Musician is playing Musician is playingCode language: CSS (css)Mixins
Next: Перечисления