Menu
Coddy logo textTech

Основы полиморфизма

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

Полиморфизм означает «много форм» — это способность работать с объектами разных классов через общий интерфейс. Когда у вас есть ссылка на родительский класс, она может указывать на любой объект дочернего класса, и вызывается соответствующий метод в зависимости от фактического типа объекта.

Это мощный инструмент, так как вы можете писать код, который работает с родительским типом, но при этом автоматически корректно обрабатывает все дочерние типы:

class Animal {
  void speak() => print('Some sound');
}

class Dog extends Animal {
  @override
  void speak() => print('Woof!');
}

class Cat extends Animal {
  @override
  void speak() => print('Meow!');
}

void main() {
  Animal myPet = Dog();  // Родительский тип, дочерний объект
  myPet.speak();  // Вывод: Woof!
  
  myPet = Cat();  // Та же переменная, другой объект
  myPet.speak();  // Вывод: Meow!
}

Переменная myPet объявлена как Animal, но она содержит Dog или Cat. Когда вызывается speak(), Dart проверяет фактический тип объекта во время выполнения и вызывает соответствующий переопределенный метод.

Это позволяет вам писать гибкие функции, которые работают с любым подклассом:

void makeAllSpeak(List<Animal> animals) {
  for (var animal in animals) {
    animal.speak();  // Каждое животное говорит по-своему
  }
}

void main() {
  var pets = [Dog(), Cat(), Dog()];
  makeAllSpeak(pets);
  // Вывод: Woof! Meow! Woof!
}

Функции не нужно знать о Dog или Cat — она просто работает с Animal. Это делает ваш код более расширяемым, так как добавление новых типов животных не требует изменений в существующих функциях.

challenge icon

Задание

Легко

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

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

  • instruments.dart: Определите здесь иерархию ваших инструментов:
    • Класс Instrument со свойством String name и конструктором. Включите метод play(), который выводит [name] makes a sound
    • Класс Guitar, который расширяет Instrument и переопределяет play() для вывода [name] strums melodically
    • Класс Drum, который расширяет Instrument и переопределяет play() для вывода [name] beats rhythmically
    • Класс Piano, который расширяет Instrument и переопределяет play() для вывода [name] plays harmoniously
    Также создайте функцию performConcert(List<Instrument> instruments), которая выводит Concert begins!, затем перебирает список, вызывая play() для каждого инструмента, и в конце выводит Concert ends!
  • main.dart: Импортируйте файл с инструментами и продемонстрируйте полиморфизм, создав смешанную группу:
    • Создайте список типа List<Instrument>, содержащий Guitar с именем 'Acoustic', Drum с именем 'Snare' и Piano с именем 'Grand'
    • Передайте этот список в функцию performConcert()

Обратите внимание, как performConcert() работает с родительским типом Instrument, но при этом каждый инструмент играет по-своему. Это и есть полиморфизм — один и тот же вызов метода приводит к разному поведению в зависимости от фактического типа объекта.

Ожидаемый вывод:

Concert begins!
Acoustic strums melodically
Snare beats rhythmically
Grand plays harmoniously
Concert ends!

Шпаргалка

Полиморфизм означает «много форм» — он позволяет работать с объектами разных классов через общий интерфейс. Ссылка на родительский класс может указывать на любой объект дочернего класса, и при выполнении программы вызывается правильный метод в зависимости от фактического типа объекта.

Базовый пример полиморфизма:

class Animal {
  void speak() => print('Some sound');
}

class Dog extends Animal {
  @override
  void speak() => print('Woof!');
}

class Cat extends Animal {
  @override
  void speak() => print('Meow!');
}

void main() {
  Animal myPet = Dog();  // Родительский тип, дочерний объект
  myPet.speak();  // Output: Woof!
  
  myPet = Cat();  // Та же переменная, другой объект
  myPet.speak();  // Output: Meow!
}

Полиморфизм позволяет создавать гибкие функции, которые работают с любым подклассом:

void makeAllSpeak(List<Animal> animals) {
  for (var animal in animals) {
    animal.speak();  // Каждое животное «говорит» по-своему
  }
}

void main() {
  var pets = [Dog(), Cat(), Dog()];
  makeAllSpeak(pets);
  // Output: Woof! Meow! Woof!
}

Функция работает с родительским типом Animal, и ей не нужно знать о конкретных дочерних классах, таких как Dog или Cat. Это делает код более расширяемым — добавление новых типов не требует изменений в существующих функциях.

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

import 'instruments.dart';

void main() {
  // TODO: Создайте List<Instrument>, содержащий:
  // — Guitar с именем 'Acoustic'
  // — Drum с именем 'Snare'
  // — Piano с именем 'Grand'
  
  
  // TODO: Вызовите performConcert() со своим списком инструментов
  
}
quiz iconПроверьте себя

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

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