Polimorfizm za pomocą interfejsów
Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 58 z 110.
W poprzedniej lekcji poznano polimorfizm poprzez dziedziczenie — używanie typu klasy nadrzędnej do pracy z obiektami klas potomnych. Polimorfizm działa jednak również za pośrednictwem interfejsów, a to podejście jest często bardziej elastyczne.
Gdy klasy implementują ten sam interfejs, możesz traktować je polimorficznie, nawet jeśli nie są ze sobą powiązane dziedziczeniem:
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 i Receipt to całkowicie niezwiązane ze sobą klasy — nie mają wspólnej klasy nadrzędnej. Jednak ponieważ wszystkie implementują Printable, możemy przechowywać je na tej samej liście i wywołać printDetails() dla każdej z nich.
Jest to potężne rozwiązanie, ponieważ pozwala definiować kontrakty zachowania bez narzucania hierarchii dziedziczenia. Każda klasa może implementować interfejs, niezależnie od tego, co rozszerza. Twój kod staje się bardziej modularny, ponieważ możesz dodawać nowe typy, które można drukować, bez modyfikowania istniejącego kodu — wystarczy zaimplementować interfejs.
Wyzwanie
ŁatwyZbudujmy system odtwarzacza multimediów, który pokazuje polimorfizm za pomocą interfejsów. Utworzysz interfejs definiujący, co znaczy „odtwarzalny”, a następnie zaimplementujesz kilka niezwiązanych ze sobą typów multimediów, które spełniają ten kontrakt — dzięki czemu można je obsługiwać jednolicie, mimo że nie mają wspólnej relacji dziedziczenia.
Podzielisz kod na dwa pliki:
media.dart: Zdefiniuj tutaj interfejs i klasy multimediów:- Klasa abstrakcyjna
Playablez dwiema metodami abstrakcyjnymi:play()iString getMediaType() - Klasa
Song, która implementujePlayable. Powinna miećString titleiString artist. Jej metodaplay()wyświetlaPlaying song: [title] by [artist], agetMediaType()zwraca'Audio' - Klasa
Video, która implementujePlayable. Powinna miećString titleiint duration(w minutach). Jej metodaplay()wyświetlaPlaying video: [title] ([duration] min), agetMediaType()zwraca'Video' - Klasa
Podcast, która implementujePlayable. Powinna miećString titleiint episode. Jej metodaplay()wyświetlaPlaying podcast: [title] - Episode [episode], agetMediaType()zwraca'Podcast'
playAll(List<Playable> playlist), która przechodzi przez listę i dla każdego elementu wyświetla[Media Type]:, a następnie wywołuje dla tego elementuplay().- Klasa abstrakcyjna
main.dart: Zaimportuj plik z multimediami i pokaż polimorfizm oparty na interfejsie:- Utwórz
List<Playable>zawierającąSongz tytułem'Imagine'i wykonawcą'John Lennon',Videoz tytułem'Dart Tutorial'i czasem trwania15orazPodcastz tytułem'Tech Talk'i numerem odcinka42 - Przekaż tę listę do
playAll()
- Utwórz
Zwróć uwagę, że Song, Video i Podcast nie mają wspólnej klasy nadrzędnej — to zupełnie niezwiązane ze sobą typy. Ponieważ jednak wszystkie implementują Playable, możesz przechowywać je na tej samej liście i obsługiwać jednolicie za pośrednictwem interfejsu.
Oczekiwany wynik:
Audio:
Playing song: Imagine by John Lennon
Video:
Playing video: Dart Tutorial (15 min)
Podcast:
Playing podcast: Tech Talk - Episode 42Spróbuj swoich sił
import 'media.dart';
void main() {
// TODO: Utwórz List<Playable> zawierającą:
// - Song z tytułem 'Imagine' i wykonawcą 'John Lennon'
// - Video z tytułem 'Dart Tutorial' i czasem trwania 15
// - Podcast z tytułem 'Tech Talk' i numerem odcinka 42
// TODO: Przekaż listę do playAll()
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP
Pliki zewnętrzneBiblioteki i importyWprowadzenie do OOPKlasy a obiektySłowo kluczowe thisMetodyZmienne instancjiPodstawy konstruktorówPowtórka – prosty kalkulator4Bezpieczeństwo wartości null
Wprowadzenie do bezpieczeństwa wartości nullTypy dopuszczające null i niedopuszczające nullOperatory ? i !Słowo kluczowe late a bezpieczeństwo wartości nullOperatory uwzględniające nullBezpieczeństwo wartości null w klasachPodsumowanie — system profili użytkowników7Klasy abstrakcyjne i interfejsy
Klasy abstrakcyjneMetody abstrakcyjneInterfejsy w DartInterfejsy niejawneImplementowanie a rozszerzanieWiele interfejsówPowtórka — kalkulator kształtów10Kolekcje i typy generyczne
Przegląd List, Set i MapKolekcje bezpieczne typowoKlasy generyczneMetody generyczneOgraniczenia typów generycznychIterable i IteratorPowtórka — generyczne przechowywanie danych13Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieMetody rozszerzająceKlasy wywoływalneKlasy zapieczętowane (Dart 3)Rekordy (Dart 3)Wzorce i dopasowywanie (3.0)Typy wyliczeniowe z metodami16Projekt: Zarządzanie biblioteką
Omówienie projektuKlasy książek i użytkowników2Konstruktory w Dart
Konstruktor domyślnyKonstruktory nazwaneListy inicjalizująceKonstruktory stałeKonstruktory fabryczneKonstruktory przekierowującePodsumowanie — budowanie kształtów5Hermetyzacja
Elementy publiczne a prywatneKonwencja prefiksu _Prywatność na poziomie bibliotekiGettery i settery — szczegółowoUkrywanie informacjiPodsumowanie — dane uczniów8Domieszki
Wprowadzenie do domieszekTworzenie domieszekKorzystanie z wielu domieszekSłowo kluczowe on w domieszkachDomieszka a dziedziczenieDomieszka a interfejsPodsumowanie – system zwierząt11Metody specjalne
Nadpisanie toString()Nadpisanie hashCode i ==Interfejs ComparableMetoda call()Nadpisanie noSuchMethodPodsumowanie — własna kolekcja14Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy3Właściwości klas
Elementy instancji a elementy statycznePola final i constZmienne inicjalizowane z opóźnieniemMetody i pola statyczneGettery i setteryPowtórzenie – menedżer kont bankowych6Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superPrzesłanianie metodAdnotacja @overrideSłowo kluczowe final dla klasKonstruktory i dziedziczeniePowtórzenie – hierarchia pracowników9Polimorfizm
Podstawy polimorfizmuPolimorfizm za pomocą interfejsówSprawdzanie typu w czasie działaniaOperatory is i asSłowo kluczowe covariantPodsumowanie — procesor płatnościPoćwicz samodzielnie: Kompilator Dart online