Menu
Coddy logo textTech

Полиморфизм через интерфейсы

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

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

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

abstract class Printable {
  void printDetails();
}

class Invoice implements Printable {
  @override
  void printDetails() => print('Invoice #1234');
}

class Report implements Printable {
  @override
  void printDetails() => print('Monthly Report');
}

class Receipt implements Printable {
  @override
  void printDetails() => print('Receipt for purchase');
}

void printAll(List<Printable> items) {
  for (var item in items) {
    item.printDetails();
  }
}

void main() {
  var documents = [Invoice(), Report(), Receipt()];
  printAll(documents);
}

Invoice, Report и Receipt — это совершенно не связанные классы, у них нет общего родителя. Тем не менее, поскольку все они реализуют Printable, мы можем хранить их в одном списке и вызывать printDetails() для каждого из них.

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

challenge icon

Задание

Легко

Давайте создадим систему медиаплеера, которая демонстрирует полиморфизм через интерфейсы. Вы создадите интерфейс, определяющий, что значит быть «воспроизводимым» (playable), а затем реализуете несколько несвязанных типов медиа, которые выполняют этот контракт — это позволит обрабатывать их единообразно, несмотря на отсутствие отношений наследования.

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

  • media.dart: Определите здесь свой интерфейс и классы медиа:
    • Абстрактный класс Playable с двумя абстрактными методами: play() и String getMediaType()
    • Класс Song, который реализует Playable. Он должен иметь String title и String artist. Его метод play() выводит Playing song: [title] by [artist], а getMediaType() возвращает 'Audio'
    • Класс Video, который реализует Playable. Он должен иметь String title и int duration (в минутах). Его метод play() выводит Playing video: [title] ([duration] min), а getMediaType() возвращает 'Video'
    • Класс Podcast, который реализует Playable. Он должен иметь String title и int episode. Его метод play() выводит Playing podcast: [title] - Episode [episode], а getMediaType() возвращает 'Podcast'
    Также создайте функцию playAll(List<Playable> playlist), которая перебирает список и для каждого элемента выводит [Media Type]:, после чего вызывает play() для этого элемента.
  • main.dart: Импортируйте файл медиа и продемонстрируйте полиморфизм на основе интерфейсов:
    • Создайте List<Playable>, содержащий Song с названием 'Imagine' и исполнителем 'John Lennon', Video с названием 'Dart Tutorial' и длительностью 15, и Podcast с названием 'Tech Talk' и эпизодом 42
    • Передайте этот список в playAll()

Обратите внимание, что Song, Video и Podcast не имеют общего родительского класса — это совершенно не связанные типы. Тем не менее, поскольку все они реализуют Playable, вы можете хранить их в одном списке и работать с ними единообразно через интерфейс.

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

Audio:
Playing song: Imagine by John Lennon
Video:
Playing video: Dart Tutorial (15 min)
Podcast:
Playing podcast: Tech Talk - Episode 42

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

import 'media.dart';

void main() {
  // TODO: Создайте List<Playable>, содержащий:
  // - Song с названием 'Imagine' и исполнителем 'John Lennon'
  // - Video с названием 'Dart Tutorial' и длительностью 15
  // - Podcast с названием 'Tech Talk' и эпизодом 42
  
  // TODO: Передайте этот список в playAll()
}
quiz iconПроверьте себя

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

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