Введение в Mixins
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 50 из 110.
Вы узнали, что Dart позволяет расширять только один класс, и хотя вы можете реализовывать несколько интерфейсов, вам придется писать весь код самостоятельно. Но что, если вы хотите разделить фактический код реализации между несколькими несвязанными классами? Именно здесь на помощь приходят миксины (mixins).
Примесь (mixin) — это способ повторного использования кода в нескольких иерархиях классов. Представьте это как набор методов и свойств, которые можно «подмешать» в любой класс, наделяя его новыми возможностями без использования наследования.
mixin Swimming {
void swim() {
print('Swimming through the water');
}
}
class Duck with Swimming {
void quack() {
print('Quack!');
}
}
class Fish with Swimming {
void blowBubbles() {
print('Blub blub');
}
}
void main() {
var duck = Duck();
duck.quack();
duck.swim(); // Из примеси (mixin)
var fish = Fish();
fish.swim(); // Та же реализация, другой класс
}Обратите внимание на ключевое слово with — именно так вы применяете миксин к классу. В отличие от implements, вам не нужно писать метод самостоятельно. Миксин предоставляет сам код, и ваш класс автоматически получает эту функциональность.
Примеси решают реальную проблему: Duck и Fish не связаны через наследование, но им обоим требуется поведение плавания. С помощью примесей вы пишете код один раз и используете его везде, где это необходимо.
Задание
ЛегкоДавайте создадим систему музыкальных инструментов, которая демонстрирует, как миксины позволяют разделять поведение между несвязанными классами. Вы создадите миксин, обеспечивающий функциональность настройки, а затем примените его к различным инструментам, которые не связаны через наследование.
Вы организуете свой код в два файла:
instruments.dart: Определите здесь свой миксин и классы инструментов:- Миксин
Tunableс методомtune(), который выводитTuning the instrument..., и методомcheckTuning(), который выводитTuning is perfect! - Класс
Guitar, использующий миксинTunable. Добавьте свойствоString brandс конструктором. Включите методstrum(), который выводит[brand] guitar strumming chords - Класс
Piano, который также использует миксинTunable. Добавьте свойствоint keysс конструктором. Включите методplay(), который выводитPlaying [keys]-key piano
- Миксин
main.dart: Импортируйте файл с инструментами и продемонстрируйте, как оба несвязанных инструмента получают возможности настройки из одного и того же миксина:- Создайте
Guitarс брендом'Fender' - Создайте
Pianoс88клавишами - Для гитары: вызовите
tune(), затемstrum(), затемcheckTuning() - Выведите пустую строку
- Для пианино: вызовите
tune(), затемplay(), затемcheckTuning()
- Создайте
Обратите внимание, что Guitar и Piano — это совершенно разные инструменты без иерархии наследования, однако они оба получают одни и те же методы настройки из миксина Tunable. Вы пишете код настройки один раз, и оба класса могут использовать его с помощью ключевого слова with.
Ожидаемый вывод:
Tuning the instrument...
Fender guitar strumming chords
Tuning is perfect!
Tuning the instrument...
Playing 88-key piano
Tuning is perfect!Шпаргалка
Миксин (mixin) — это способ повторного использования кода в нескольких несвязанных классах без использования наследования. Это набор методов и свойств, которые вы можете «подмешать» в любой класс.
Определите миксин с помощью ключевого слова mixin:
mixin Swimming {
void swim() {
print('Swimming through the water');
}
}Примените миксин к классу с помощью ключевого слова with:
class Duck with Swimming {
void quack() {
print('Quack!');
}
}
class Fish with Swimming {
void blowBubbles() {
print('Blub blub');
}
}В отличие от implements, вам не нужно писать реализацию метода самостоятельно — миксин предоставляет готовый код, и ваш класс автоматически получает эту функциональность.
Теперь и Duck, и Fish могут использовать метод swim(), хотя они не связаны через наследование:
var duck = Duck();
duck.swim(); // Из миксина
var fish = Fish();
fish.swim(); // Та же реализация, другой классПопробуйте сами
import 'instruments.dart';
void main() {
// TODO: Создайте Guitar с брендом 'Fender'
// TODO: Создайте Piano с 88 клавишами
// TODO: Для гитары: вызовите tune(), затем strum(), затем checkTuning()
// TODO: Выведите пустую строку
// TODO: Для пианино: вызовите tune(), затем play(), затем checkTuning()
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий