Menu
Coddy logo textTech

Composizione vs ereditarietà

Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 76 di 104.

Quando progetti relazioni tra classi, hai due opzioni principali: ereditarietà («è un») e composizione («ha un»). Scegliere l’approccio giusto influisce notevolmente sulla flessibilità e sulla manutenibilità del tuo codice.

L'ereditarietà crea un forte accoppiamento tra le classi. Una Car che eredita da Vehicle è legata in modo permanente a questa relazione:

class Vehicle {
public:
    virtual void start() { std::cout << "Starting vehicle\n"; }
};

class Car : public Vehicle {
public:
    void start() override { std::cout << "Starting car\n"; }
};

Composizione incorpora oggetti come membri, creando una relazione più flessibile. Un Car ha un Engine invece di essere un motore:

class Engine {
public:
    void start() { std::cout << "Engine running\n"; }
};

class Car {
private:
    Engine engine;  // Car possiede un Engine
public:
    void start() { engine.start(); }
};

Il vantaggio principale della composizione è la flessibilità. Puoi sostituire facilmente i componenti, modificare il comportamento durante l’esecuzione o combinare più funzionalità senza i vincoli di una gerarchia di classi rigida. Nella maggior parte dei casi, la progettazione moderna in C++ privilegia la composizione rispetto all’ereditarietà.

Usa l'ereditarietà quando c'è una vera relazione «è un» e hai bisogno del polimorfismo. Usa la composizione quando vuoi riutilizzare funzionalità o quando la relazione è descritta meglio come «ha un» o «usa un».

challenge icon

Sfida

Facile

Costruiamo un simulatore di sistema informatico che mostri quando usare la composizione invece dell'ereditarietà. Modellerai un computer che ha dei componenti (composizione), anziché essere un componente, mostrando come la composizione offra la flessibilità di sostituire le parti durante l'esecuzione.

Organizzerai il codice in tre file:

  • Components.h: definisci i singoli componenti che un computer può contenere.

    Crea una classe CPU con un std::string model privato e un int cores. Fornisci un costruttore che accetti entrambi i valori, un metodo process() che stampi CPU [model] processing with [cores] cores e un metodo getModel().

    Crea una classe Memory con un int sizeGB privato. Fornisci un costruttore, un metodo load() che stampi Loading data into [sizeGB]GB RAM e un metodo getSize().

    Crea una classe Storage con un std::string type privato (come "SSD" o "HDD") e un int capacityGB. Fornisci un costruttore, un metodo read() che stampi Reading from [capacityGB]GB [type] e metodi getter per entrambi i campi.

  • Computer.h: definisci una classe Computer che utilizzi la composizione per combinare i componenti.

    La classe Computer dovrebbe avere oggetti membro privati: una CPU, una Memory e uno Storage. Usa la lista di inizializzazione del costruttore per inizializzare questi componenti a partire dai parametri.

    Implementa questi metodi:

    • boot(): stampa Booting computer..., poi chiama load() sulla memoria, read() sullo storage e process() sulla CPU, ciascuno su una riga separata
    • specs(): stampa le specifiche del computer in questo formato:
      Computer Specs:
      - CPU: [model] ([cores] cores)
      - RAM: [size]GB
      - Storage: [capacity]GB [type]
  • main.cpp: leggi sei input (ciascuno su una riga separata):
    1. Nome del modello della CPU (stringa)
    2. Numero di core della CPU (intero)
    3. Dimensione della memoria in GB (intero)
    4. Tipo di storage (stringa, ad esempio "SSD")
    5. Capacità dello storage in GB (intero)
    6. Un comando: "boot" o "specs"

    Crea gli oggetti dei componenti, poi crea un Computer usando la composizione. In base al comando, chiama boot() oppure specs().

Ad esempio, con gli input Intel i7, 8, 16, SSD, 512 e boot:

Booting computer...
Loading data into 16GB RAM
Reading from 512GB SSD
CPU Intel i7 processing with 8 cores

Con gli input AMD Ryzen, 6, 32, HDD, 1000 e specs:

Computer Specs:
- CPU: AMD Ryzen (6 cores)
- RAM: 32GB
- Storage: 1000GB HDD

Nota che la classe Computer non eredita da alcun componente. Li contiene. Questa relazione «ha un» significa che potresti facilmente creare computer con diverse combinazioni di componenti o persino sostituire i componenti in seguito, aggiungendo metodi setter. Questa flessibilità è il vantaggio principale della composizione rispetto all'ereditarietà.

Provalo tu

#include <iostream>
#include <string>
#include "Computer.h"

using namespace std;

int main() {
    // Leggi gli input
    string cpuModel;
    getline(cin, cpuModel);  // Nome del modello della CPU (può contenere spazi)
    
    int cpuCores;
    cin >> cpuCores;  // Numero di core della CPU
    
    int memorySize;
    cin >> memorySize;  // Dimensione della memoria in GB
    
    string storageType;
    cin >> storageType;  // Tipo di archiviazione (ad es., "SSD")
    
    int storageCapacity;
    cin >> storageCapacity;  // Capacità di archiviazione in GB
    
    string command;
    cin >> command;  // Comando: "boot" o "specs"

    // TODO: Crea gli oggetti dei componenti (CPU, Memory, Storage)
    
    // TODO: Crea un oggetto Computer usando la composizione
    
    // TODO: In base al comando, chiama boot() oppure specs()

    return 0;
}
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 C++ online