Menu
Coddy logo textTech

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.

challenge icon

Sfida

Facile

Costruiamo 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) e speedGHz (double). Includi un costruttore per inizializzare entrambi i campi e i metodi getter per ciascuno. Aggiungi un metodo process() che restituisca una String: [brand] processing at [speedGHz] GHz
  • Memory.java: Crea una classe che rappresenti la RAM. Una Memory ha due campi privati: type (String, come "DDR4" o "DDR5") e sizeGB (int). Includi un costruttore, i getter e un metodo load() che restituisca: Loading [sizeGB]GB [type] memory
  • Computer.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 metodo boot() che restituisca una String su più righe che mostri l'avvio del computer usando i suoi componenti:
    Booting computer...
    [processor.process() result]
    [memory.load() result]
    System ready!
    Aggiungi anche un metodo 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 di getSpecs().

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()
    }
}
quiz iconMettiti alla prova

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

Esercitati da solo: Compilatore Java online