Polimorfismo tramite interfacce
Fa parte della sezione Programmazione orientata agli oggetti del percorso Dart di Coddy. Lezione 58 di 110.
Nella lezione precedente, hai visto il polimorfismo tramite l'ereditarietà: usare il tipo di una classe genitore per lavorare con oggetti figlio. Ma il polimorfismo funziona anche tramite le interfacce e questo approccio è spesso più flessibile.
Quando le classi implementano la stessa interfaccia, puoi trattarle in modo polimorfico anche se non hanno alcuna relazione di ereditarietà:
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 e Receipt sono classi completamente non correlate: non condividono una classe genitore. Eppure, poiché implementano tutte Printable, possiamo memorizzarle nella stessa lista e chiamare printDetails() su ciascuna.
Questo è potente perché ti permette di definire contratti di comportamento senza imporre una gerarchia di ereditarietà. Qualsiasi classe può implementare un'interfaccia, indipendentemente da ciò che estende. Il tuo codice diventa più modulare, poiché puoi aggiungere nuovi tipi stampabili senza modificare il codice esistente: basta implementare l'interfaccia.
Sfida
FacileCostruiamo un sistema di riproduzione multimediale che dimostri il polimorfismo tramite interfacce. Creerai un'interfaccia che definisce cosa significa essere «riproducibile», poi implementerai diversi tipi di contenuti multimediali non correlati che rispettano tutti questo contratto, consentendo di gestirli in modo uniforme nonostante non abbiano alcuna relazione di ereditarietà.
Organizzerai il codice in due file:
media.dart: definisci qui la tua interfaccia e le classi multimediali:- Una classe astratta
Playablecon due metodi astratti:play()eString getMediaType() - Una classe
Songche implementaPlayable. Dovrebbe avereString titleeString artist. Il suo metodoplay()stampaPlaying song: [title] by [artist]egetMediaType()restituisce'Audio' - Una classe
Videoche implementaPlayable. Dovrebbe avereString titleeint duration(in minuti). Il suo metodoplay()stampaPlaying video: [title] ([duration] min)egetMediaType()restituisce'Video' - Una classe
Podcastche implementaPlayable. Dovrebbe avereString titleeint episode. Il suo metodoplay()stampaPlaying podcast: [title] - Episode [episode]egetMediaType()restituisce'Podcast'
playAll(List<Playable> playlist)che scorre l'elenco e, per ogni elemento, stampa[Media Type]:e poi chiamaplay()su quell'elemento.- Una classe astratta
main.dart: importa il tuo file multimediale e mostra il polimorfismo basato sulle interfacce:- Crea una
List<Playable>contenente unSongcon titolo'Imagine'e artista'John Lennon', unVideocon titolo'Dart Tutorial'e durata15, e unPodcastcon titolo'Tech Talk'ed episodio42 - Passa questo elenco a
playAll()
- Crea una
Nota che Song, Video e Podcast non condividono alcuna classe genitore: sono tipi del tutto indipendenti. Tuttavia, poiché implementano tutti Playable, puoi inserirli nello stesso elenco e trattarli in modo uniforme tramite l'interfaccia.
Output previsto:
Audio:
Playing song: Imagine by John Lennon
Video:
Playing video: Dart Tutorial (15 min)
Podcast:
Playing podcast: Tech Talk - Episode 42Provalo tu
import 'media.dart';
void main() {
// TODO: Crea una List<Playable> contenente:
// - Una Song con title 'Imagine' e artist 'John Lennon'
// - Un Video con title 'Dart Tutorial' e duration 15
// - Un Podcast con title 'Tech Talk' e episode 42
// TODO: Passa la lista a playAll()
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniLibrerie e importazioniIntroduzione alla programmazione orientata agli oggettiClassi e oggettiLa parola chiave thisMetodiVariabili di istanzaFondamenti dei costruttoriRiepilogo - Calcolatrice semplice4Sicurezza dei valori null
Introduzione alla sicurezza dei valori nullNullable e non nullableGli operatori ? e !La keyword late e la sicurezza dei valori nullOperatori null-awareSicurezza dei valori null nelle classiRiepilogo - Sistema di profili utente7Classi astratte e interfacce
Classi astratteMetodi astrattiInterfacce in DartInterfacce impliciteImplementare vs estendereInterfacce multipleRiepilogo - Calcolatore di forme10Collezioni e generici
Panoramica di List, Set e MapCollezioni sicure rispetto ai tipiClassi genericheMetodi genericiVincoli sui genericiIterable e IteratorRiepilogo - Archiviazione generica13Concetti avanzati di OOP
Composizione vs ereditarietàMetodi di estensioneClassi chiamabiliClassi sealed (Dart 3)Record (Dart 3)Pattern e matching (3.0)Enum con metodi16Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente2I costruttori in Dart
Costruttore predefinitoCostruttori con nomeListe di inizializzazioneCostruttori costantiCostruttori factoryCostruttori di reindirizzamentoRiepilogo - Crea forme5Incapsulamento
Membri pubblici e privatiConvenzione del prefisso _Visibilità a livello di libreriaApprofondimento su getter e setterNascondimento delle informazioniRiepilogo - Registri degli studenti8Mixin
Introduzione ai MixinCreare MixinUsare più MixinParola chiave on nei MixinMixin vs ereditarietàMixin vs interfacciaRiepilogo - Sistema animale11Metodi speciali
Sovrascrittura di toString()Sovrascrittura di hashCode e ==Interfaccia ComparableMetodo call()Sovrascrittura di noSuchMethodRiepilogo - Collezione personalizzata14Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneSingletonFactoryObserverStrategy3Proprietà delle classi
Membri di istanza e staticiCampi final e constVariabili lateMetodi e campi staticiGetter e setterRiepilogo - Gestore di conti bancari6Ereditarietà
Ereditarietà di baseLa parola chiave superOverride dei metodiL’annotazione @overrideLa parola chiave final per le classiCostruttori ed ereditarietàRiepilogo - Gerarchia dei dipendenti9Polimorfismo
Fondamenti di polimorfismoPolimorfismo tramite interfacceControllo del tipo a runtimeGli operatori is e asParola chiave covariantRiepilogo - Elaboratore di pagamenti12OOP asincrona
Future e async/awaitNozioni di base sugli streamController degli streamCostruttori asincroniAsync nei metodi di classeRiepilogo - Recupero dei dati15Pattern di progettazione Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern RepositoryEsercitati da solo: Compilatore Dart online