Mixins

Что такое 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.

Обязательные ограничения

  1. mixin / mixin class не поддерживает оператор extends
  2. Нельзя объявлять генеративные конструкторы
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

  1. mixin: используется только через with, наследование через extends запрещено
  2. class: поддерживает extends, нельзя напрямую использовать как mixin
  3. mixin class:
    • можно использовать как Mixin с оператором with
    • можно использовать как обычный родительский класс через extends

Ограничения сохраняются:

  • 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 playing
Code language: CSS (css)

Mixins

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *