Composizione vs ereditarietà
Fa parte della sezione Programmazione orientata agli oggetti del percorso Dart di Coddy. Lezione 82 di 110.
Hai imparato che l'ereditarietà crea una relazione "è-un": un Dog è un Animal. Ma c'è un altro approccio potente: la composizione, che crea una relazione "ha-un". Invece di ereditare comportamenti, una classe contiene istanze di altre classi e delega loro il lavoro.
Considera una classe Car. Con l'ereditarietà, potresti provare a estendere una classe Engine, ma un'auto non è un motore: ha un motore:
class Engine {
void start() => print('Engine started');
}
class Car {
final Engine _engine = Engine(); // Composizione: Car HA un Engine
void start() {
_engine.start(); // Delega al motore
print('Car is ready to drive');
}
}
void main() {
var car = Car();
car.start();
}Il vantaggio principale della composizione è la flessibilità. Con l’ereditarietà, sei vincolato a una singola classe genitore. Con la composizione, puoi combinare più componenti e persino sostituirli durante l’esecuzione:
class ElectricEngine {
void start() => print('Electric motor humming');
}
class HybridCar {
Engine? _gasEngine;
ElectricEngine? _electricEngine;
HybridCar({bool useElectric = false}) {
if (useElectric) {
_electricEngine = ElectricEngine();
} else {
_gasEngine = Engine();
}
}
}Una linea guida comune è "preferire la composizione all'ereditarietà". Usa l'ereditarietà quando esiste una vera relazione "è un" e hai bisogno del polimorfismo. Usa la composizione quando vuoi riutilizzare un comportamento senza un accoppiamento stretto, oppure quando un oggetto ha bisogno di funzionalità provenienti da più fonti.
Sfida
FacileCostruiamo un sistema informatico usando la composizione! Invece di cercare di far ereditare un computer dalle sue parti, creerai classi di componenti separate e le comporrai insieme: poiché un computer ha una CPU e ha memoria, non è una CPU né memoria.
Organizzerai il codice in tre file:
components.dart: crea due classi di componenti che rappresentano parti del computer:- Una classe
CPUcon unaString brande unadouble speedGHz. Includi un metodoprocess()che restituisce una stringa:[brand] processing at [speedGHz]GHz - Una classe
Memorycon unint sizeGB. Includi un metodoload()che restituisce una stringa:Loading [sizeGB]GB of data
- Una classe
computer.dart: crea una classeComputerche usa la composizione per combinare i componenti. Il tuo computer dovrà:- Avere un campo
String name - Contenere un'istanza di
CPUe una diMemory(composizione: il computer HA queste parti) - Accettare tutti i valori necessari tramite il costruttore per creare entrambi i componenti internamente
- Avere un metodo
boot()che delega il lavoro a entrambi i componenti e stampa tre righe: il nome del computer seguito dastarting..., poi il risultato del metodo process della CPU e infine il risultato del metodo load della Memory - Avere un metodo
specs()che restituisce una stringa con le specifiche del sistema:[name]: [brand] [speedGHz]GHz, [sizeGB]GB RAM
- Avere un campo
main.dart: importa i tuoi file e mostra la composizione in azione:- Crea un
ComputerchiamatoWorkstationcon una CPUIntelche funziona a3.5GHz e16GB di memoria - Chiama il metodo
boot() - Stampa il risultato di
specs()
- Crea un
Nota che la classe Computer non estende CPU né Memory: li contiene e delega loro il lavoro. Questa è la relazione "has-a" creata dalla composizione!
Output previsto:
Workstation starting...
Intel processing at 3.5GHz
Loading 16GB of data
Workstation: Intel 3.5GHz, 16GB RAMProvalo tu
import 'computer.dart';
void main() {
// TODO: Crea un Computer chiamato "Workstation" con:
// - una CPU Intel a 3.5 GHz
// - 16 GB di memoria
// TODO: Chiama il metodo boot()
// TODO: Stampa il risultato di specs()
}
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