Composizione vs ereditarietà
Fa parte della sezione Programmazione orientata agli oggetti del percorso Java di Coddy. Lezione 47 di 87.
L’ereditarietà crea una relazione «è un»: un Dog è un Animal. Ma cosa succede se una classe ha bisogno delle funzionalità di qualcosa che non è? È qui che entra in gioco la composizione, che stabilisce invece una relazione «ha un».
Con la composizione, una classe contiene istanze di altre classi come campi invece di estenderle:
// Approccio con ereditarietà: Car È un Engine (non ha senso)
class Car extends Engine { }
// Approccio con composizione: Car HA un Engine (ha senso)
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.ignite();
}
}La composizione offre diversi vantaggi. Puoi modificare l'oggetto composto durante l'esecuzione, ad esempio sostituendo i motori.
Puoi anche combinare i comportamenti di più classi, cosa che l’ereditarietà non consente in Java. La classe composta espone solo i metodi che sceglie, garantendo un incapsulamento migliore.
Una linea guida pratica: usa l’ereditarietà quando esiste una vera relazione «è un» e la sottoclasse rappresenta davvero una versione specializzata della classe genitore.
Usa la composizione quando hai bisogno di utilizzare le funzionalità di un’altra classe senza essere di quel tipo. Molti sviluppatori esperti seguono il principio: "preferisci la composizione all’ereditarietà" perché porta a un codice più flessibile e facile da mantenere.
Sfida
FacileCostruiamo un sistema informatico usando la composizione per dimostrare la relazione "has-a". Invece di far ereditare Computer dai suoi componenti (cosa che non avrebbe senso), il tuo computer conterrà le parti necessarie per funzionare.
Organizzerai il codice in tre file:
Processor.java: Crea una classe che rappresenti una CPU. Un Processor ha due campi privati:brand(String) espeedGHz(double). Includi un costruttore per inizializzare entrambi i campi e i metodi getter per ciascuno. Aggiungi un metodoprocess()che restituisca una String:[brand] processing at [speedGHz] GHzMemory.java: Crea una classe che rappresenti la RAM. Una Memory ha due campi privati:type(String, come "DDR4" o "DDR5") esizeGB(int). Includi un costruttore, i getter e un metodoload()che restituisca:Loading [sizeGB]GB [type] memoryComputer.java: È qui che la composizione dà il meglio di sé! La tua classe Computer dovrebbe avere un Processor e una Memory come campi privati. Non li estende: li contiene. Includi un costruttore che accetti entrambi i componenti. Aggiungi un metodoboot()che restituisca una String su più righe che mostri l'avvio del computer usando i suoi componenti:Aggiungi anche un metodoBooting computer... [processor.process() result] [memory.load() result] System ready!getSpecs()che restituisca:Specs: [processor brand] CPU, [memory sizeGB]GB [memory type]Main.java: Dai vita al tuo sistema composto! Riceverai quattro input: marca del processore (String), velocità del processore (double), tipo di memoria (String) e dimensione della memoria (int).Crea un Processor e una Memory con questi valori, quindi componili in un Computer. Stampa il risultato della chiamata a
boot(), poi stampa il risultato digetSpecs().
Riceverai quattro input, in quest'ordine: marca del processore, velocità del processore (GHz), tipo di memoria e dimensione della memoria (GB).
Nota come il Computer deleghi il lavoro ai suoi componenti invece di cercare di essere quei componenti. Questa è la potenza della composizione! Il Computer può usare qualsiasi Processor o Memory gli venga fornito, rendendo il design flessibile e realistico.
Provalo tu
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leggi i dati in input
String processorBrand = scanner.nextLine();
double processorSpeed = Double.parseDouble(scanner.nextLine());
String memoryType = scanner.nextLine();
int memorySize = Integer.parseInt(scanner.nextLine());
// TODO: Crea un Processor con la marca e la velocità
// TODO: Crea una Memory con il tipo e la dimensione
// TODO: Crea un Computer componendo Processor e Memory
// TODO: Stampa il risultato di boot()
// TODO: Stampa il risultato di getSpecs()
}
}
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 a oggetti
File esterniIntroduzione alla programmazione a oggettiClassi e oggettiLa parola chiave thisMetodiCampi (attributi)Metodo costruttoreSovraccarico dei costruttoriRipasso - Calcolatrice semplice4Ereditarietà
Ereditarietà di base (extends)La parola chiave superSovrascrittura dei metodi (@Override)Concatenamento dei costruttoriLa classe ObjectEreditarietà singola e multilivelloPerché non esiste l'ereditarietà multipla delle classiRiepilogo: gerarchia dei dipendenti7Metodi speciali e classe Object
Metodo toString()equals() e hashCode()Metodo clone()compareTo() e ComparableInterfaccia ComparatorRiepilogo - Ordinamento personalizzato2Modificatori di accesso e incapsulamento
Panoramica dei livelli di accessoMetodi getter e setterNascondimento delle informazioniLa parola chiave finalRiepilogo - Gestore di conti bancari5Polimorfismo
Nozioni di base sull'overloading dei metodiOverride dei metodi (a runtime)Upcasting e downcastingL'operatore instanceofClassi e metodi astrattiRiepilogo - Calcolatore di forme8Concetti avanzati di OOP
Composizione vs ereditarietàAggregazione vs composizioneClassi interne, annidate e anonimeEnum e metodi degli EnumRecord (Java 16+)Classi sealed (Java 17+)11Modelli di progettazione Parte 1
Introduzione ai modelli di progettazionePattern SingletonPattern FactoryPattern BuilderPattern ObserverPattern Strategy3Proprietà di classe e membri statici
Variabili di istanza e staticheMetodi staticiBlocchi staticiCostanti (static final)Riepilogo - Contatore e utility6Interfacce e classi astratte
Introduzione alle interfacceImplementazione delle interfacceImplementazione di più interfacceMetodi default e static nelle interfacceClassi astratte e interfacce a confrontoInterfacce funzionaliRiepilogo: sistema di pagamento9Tipi generici
Introduzione ai tipi genericiClassi genericheMetodi genericiParametri di tipo con limitiCaratteri jolly (?, extends, super)Riepilogo - Contenitore generico12Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern IteratorEsercitati da solo: Compilatore Java online