Menu
Coddy logo textTech

Mixin и наследование

Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 54 из 110.

Теперь, когда вы узнали, как работают миксины, давайте проясним, когда их следует использовать вместо наследования. Ключевое отличие заключается в отношениях, которые они выражают.

Наследование представляет собой отношение "is-a". Dog является Animal. Подкласс — это специализированная версия родительского класса, и вы можете расширять только один класс.

Миксины, с другой стороны, представляют собой отношение «может делать». Dog может плавать, но плавание — это не то, что определяет собаку.

// Наследование: Dog ЯВЛЯЕТСЯ Animal
class Animal {
  String name;
  Animal(this.name);
}

class Dog extends Animal {
  Dog(String name) : super(name);
}

// Mixin: Dog МОЖЕТ плавать (но не определяется плаванием)
mixin Swimming {
  void swim() => print('Swimming');
}

class Dog extends Animal with Swimming {
  Dog(String name) : super(name);
}

Используйте наследование, когда классы имеют общую основу и вам нужны конструкторы в родительском классе. Используйте примеси (mixins), когда вы хотите добавить функциональные возможности классам, не имеющим общего предка.

ОсобенностьНаследованиеПримесь (Mixin)
Тип связиявляется (is-a)может (can-do)
ОграничениеТолько один родительНесколько примесей
КонструкторыРазрешеныНе разрешены
Вариант использованияСпециализацияОбщее поведение

И что самое приятное? Вы можете комбинировать и то, и другое — наследовать один класс и подмешивать несколько возможностей, наделяя ваши классы именно теми функциями, которые им необходимы.

challenge icon

Задание

Легко

Давайте создадим систему сотрудников, которая продемонстрирует, когда использовать наследование, а когда — примеси (mixins). Вы создадите базовый класс для отношений «является» (is-a) и примеси для способностей «может делать» (can-do), а затем объедините их, чтобы показать, как оба подхода работают вместе.

Вы организуете свой код в три файла:

  • worker.dart: Определите базовый класс, который устанавливает основную идентичность всех сотрудников:
    • Класс Worker со свойствами String name и String department. Включите конструктор, принимающий оба значения. Добавьте метод introduce(), который выводит '$name works in $department'
  • skills.dart: Определите примеси, которые представляют способности, которыми могут обладать сотрудники (отношения «может делать»):
    • Примесь CanCode с методом writeCode(), который выводит 'Writing code...'
    • Примесь CanDesign с методом createDesign(), который выводит 'Creating design...'
    • Примесь CanManage с методом leadTeam(), который выводит 'Leading the team...'
  • main.dart: Импортируйте оба файла и создайте специализированные типы сотрудников, сочетающие наследование с примесями:
    • Класс Developer, который расширяет Worker и использует примесь CanCode
    • Класс TechLead, который расширяет Worker и использует примеси CanCode и CanManage
    • Создайте Developer с именем 'Alice' в отделе 'Engineering'
    • Создайте TechLead с именем 'Bob' в отделе 'Engineering'
    • Для Alice: вызовите introduce(), затем writeCode()
    • Выведите пустую строку
    • Для Bob: вызовите introduce(), затем writeCode(), затем leadTeam()

Обратите внимание, что и Developer, и TechLead являются сотрудниками (наследование), но они могут делать разные вещи (примеси). TechLead объединяет несколько способностей без необходимости множественного наследования.

Ожидаемый результат:

Alice works in Engineering
Writing code...

Bob works in Engineering
Writing code...
Leading the team...

Шпаргалка

Наследование представляет собой отношение «is-a» (подкласс является специализированной версией родителя), в то время как примеси (mixins) представляют собой отношение «can-do» (добавление возможностей без определения идентичности).

// Наследование: Dog ЯВЛЯЕТСЯ Animal
class Animal {
  String name;
  Animal(this.name);
}

class Dog extends Animal {
  Dog(String name) : super(name);
}

// Примесь: Dog МОЖЕТ плавать
mixin Swimming {
  void swim() => print('Swimming');
}

class Dog extends Animal with Swimming {
  Dog(String name) : super(name);
}
ОсобенностьНаследованиеПримесь (Mixin)
Отношениеis-acan-do
ОграничениеТолько один родительМножество примесей
КонструкторыРазрешеныНе разрешены
Сценарий использованияСпециализацияОбщее поведение

Вы можете комбинировать оба подхода — расширять один класс и подмешивать несколько возможностей:

class TechLead extends Worker with CanCode, CanManage {
  TechLead(String name, String department) : super(name, department);
}

Попробуйте сами

import 'worker.dart';
import 'skills.dart';

// TODO: Создайте класс Developer, который расширяет Worker и использует примесь CanCode

// TODO: Создайте класс TechLead, который расширяет Worker и использует примеси CanCode и CanManage

void main() {
  // TODO: Создайте Developer по имени 'Alice' в отделе 'Engineering'
  
  // TODO: Вызовите introduce(), затем writeCode() для Alice
  
  // TODO: Выведите пустую строку
  
  // TODO: Создайте TechLead по имени 'Bob' в отделе 'Engineering'
  
  // TODO: Вызовите introduce(), writeCode(), затем leadTeam() для Bob
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование