CodeGym /Corsi /JAVA 25 SELF /Uso del polimorfismo nella pratica

Uso del polimorfismo nella pratica

JAVA 25 SELF
Livello 18 , Lezione 3
Disponibile

1. Polimorfismo nelle collezioni: a cosa serve?

Iniziamo con una domanda: «A cosa serve davvero il polimorfismo nelle applicazioni reali?»

Immaginate uno zoo. Avete una classe base Animal e un sacco di sottoclassi: Dog, Cat, Cow, Parrot e persino Platypus (ornitorinco, per gli amanti dell’esotico). Ognuno sa emettere un suono (makeSound()), ma lo fa a modo suo.

Invece di creare array separati per ogni animale, dichiarate un array o una lista di tipo Animal e ci mettete dentro chi volete:

Animal[] animals = {
    new Dog(),
    new Cat(),
    new Cow(),
    new Parrot()
};

Ora potete scorrere questo array e chiamare makeSound() per ognuno:

for (Animal animal : animals) {
    animal.makeSound();
}

Magia! Ogni oggetto sa da sé quale suono emettere e non dovete scrivere una marea di if o switch.

Esempio con un’analogia

È come se diceste «Voce!» a un gruppo di animali, e ognuno decidesse da solo cosa fare: il cane abbaia, il gatto miagola e la mucca — muggirà. Non specificate chi è chi — chiamate semplicemente lo stesso metodo.

2. Esempio pratico: gerarchia dei dipendenti

Rendiamo l’esempio più vicino alla realtà (e al vostro futuro lavoro nell’IT). Immaginate un’azienda con diversi dipendenti: manager, sviluppatori, tester. Ognuno ha un metodo work(), ma viene eseguito in modo diverso.

Dichiarazione della classe base

public class Employee {
    public void work() {
        System.out.println("Il dipendente lavora...");
    }
}

Sottoclassi

public class Manager extends Employee {
    @Override
    public void work() {
        System.out.println("Il manager tiene una riunione.");
    }
}

public class Developer extends Employee {
    @Override
    public void work() {
        System.out.println("Lo sviluppatore scrive codice.");
    }
}

public class Tester extends Employee {
    @Override
    public void work() {
        System.out.println("Il tester cerca bug.");
    }
}

Uso di un array/lista del tipo base

public class CompanyDemo {
    public static void main(String[] args) {
        Employee[] team = {
            new Manager(),
            new Developer(),
            new Tester(),
            new Developer()
        };

        for (Employee e : team) {
            e.work(); // Verrà chiamata la versione “corretta” del metodo per ogni oggetto
        }
    }
}

Risultato dell’esecuzione:

Il manager tiene una riunione.
Lo sviluppatore scrive codice.
Il tester cerca bug.
Lo sviluppatore scrive codice.

Quali sono i vantaggi?

  • Non scrivete un mucchio di controlli del tipo «Se è Developer — fai questo e quello».
  • Aggiungere un nuovo dipendente (per esempio, Designer) — basta creare una nuova classe e aggiungerlo all’array.
  • Il codice che usa l’array dei dipendenti non cambia affatto!

3. Vantaggi del polimorfismo: flessibilità ed estendibilità

Immaginate che nella vostra azienda compaia un nuovo tipo di dipendente — Designer. Tutto ciò che dovete fare è creare una nuova classe:

public class Designer extends Employee {
    @Override
    public void work() {
        System.out.println("Il designer disegna mockup.");
    }
}

Ora si può aggiungere il designer al team:

Employee[] team = {
    new Manager(),
    new Developer(),
    new Tester(),
    new Designer()
};

Voilà! Il programma inizia subito a funzionare correttamente con il nuovo tipo di dipendente, senza cambiare una riga del codice che scorre l’array e chiama work().

Questa è l’estendibilità: il vostro codice si adatta facilmente a nuovi tipi di oggetti.

4. Limitazioni del polimorfismo: l’altra faccia della medaglia

Purtroppo, ogni magia ha i suoi limiti (e un prezzo, come in qualsiasi RPG).

Sono disponibili solo i metodi della classe base

Quando lavorate con una variabile di tipo Employee, potete chiamare solo i metodi dichiarati nella classe Employee. Se nella classe Developer esiste un metodo specifico writeCode(), non potrete chiamarlo direttamente:

Employee e = new Developer();
// e.writeCode(); // Errore di compilazione: tale metodo non esiste in Employee!

Se proprio volete chiamare un metodo specifico, dovrete fare un cast. Ma è l’ultima risorsa. Se fate spesso cast, forse dovreste rivedere il design delle classi — la classe base o l’interfaccia dovrebbe contenere il metodo necessario.

if (e instanceof Developer) {
    Developer dev = (Developer) e;
    dev.writeCode();
}

Ma così perdete l’universalità e l’eleganza per cui tutto questo è stato pensato. Quindi cercate di progettare la classe base in modo che contenga solo quei metodi che servono davvero a tutte le sottoclassi.

5. Pratica: realizziamo una gerarchia di dipendenti

Uniremo l’utile al dilettevole: scriviamo una semplice applicazione in cui ci sono diversi tipi di dipendenti e usiamo il polimorfismo per gestirli.

Passo 1: classe base e sottoclassi

// Employee.java
public class Employee {
    public void work() {
        System.out.println("Il dipendente lavora...");
    }
}

// Manager.java
public class Manager extends Employee {
    @Override
    public void work() {
        System.out.println("Il manager tiene una riunione.");
    }
}

// Developer.java
public class Developer extends Employee {
    @Override
    public void work() {
        System.out.println("Lo sviluppatore scrive codice.");
    }
}

// Tester.java
public class Tester extends Employee {
    @Override
    public void work() {
        System.out.println("Il tester cerca bug.");
    }
}

Passo 2: classe principale

// CompanyDemo.java
public class CompanyDemo {
    public static void main(String[] args) {
        Employee[] team = {
            new Manager(),
            new Developer(),
            new Tester(),
            new Developer()
        };

        for (Employee e : team) {
            e.work();
        }
    }
}

Passo 3: aggiungiamo estendibilità

Supponiamo che tra un mese in azienda compaia un nuovo dipendente — un designer. Ecco tutto ciò che serve:

public class Designer extends Employee {
    @Override
    public void work() {
        System.out.println("Il designer disegna mockup.");
    }
}

Fatto, ora si può aggiungere il designer al team:

Employee[] team = {
    new Manager(),
    new Developer(),
    new Tester(),
    new Designer()
};

Conclusione

Tutto il codice principale (CompanyDemo) è rimasto invariato! Questa è la forza del polimorfismo.

Errori tipici nell’uso del polimorfismo

Errore n° 1: aspettarsi di accedere a metodi specifici tramite un riferimento del tipo base.
Molto spesso i principianti cercano di chiamare metodi specifici della sottoclasse tramite una variabile del tipo superclasse. Per esempio:

Employee e = new Developer();
// e.writeCode(); // Errore! Questo metodo non è definito in Employee.

Per chiamare un metodo specifico bisogna fare un cast, ma così si perde l’universalità.

Errore n° 2: non usare l’annotazione @Override.
Se vi dimenticate l’annotazione, potreste scrivere accidentalmente un metodo nuovo invece di sovrascriverne uno (per esempio, sbagliando nome). In tal caso il polimorfismo semplicemente non funzionerà e verrà chiamata la versione della superclasse.

Errore n° 3: assenza di un’interfaccia comune.
Se la classe base non contiene il metodo necessario, il polimorfismo non è possibile. Per esempio, se in Employee non c’è il metodo work(), allora il ciclo sull’array dei dipendenti non potrà chiamarlo per tutti.

Errore n° 4: violazione del principio di apertura/chiusura.
Se per aggiungere un nuovo tipo di dipendente bisogna modificare il codice che scorre l’array/lista, significa che non state usando correttamente il polimorfismo.

1
Compito
JAVA 25 SELF, livello 18, lezione 3
Bloccato
Voci dell'enciclopedia digitale degli animali
Voci dell'enciclopedia digitale degli animali
1
Compito
JAVA 25 SELF, livello 18, lezione 3
Bloccato
Sinfonia aziendale: Polimorfismo nel lavoro dei dipendenti
Sinfonia aziendale: Polimorfismo nel lavoro dei dipendenti
1
Compito
JAVA 25 SELF, livello 18, lezione 3
Bloccato
Estensione del team: aggiunta di un nuovo tipo di dipendente
Estensione del team: aggiunta di un nuovo tipo di dipendente
1
Compito
JAVA 25 SELF, livello 18, lezione 3
Bloccato
Ufficio dinamico: estendibilità e versatilità del polimorfismo
Ufficio dinamico: estendibilità e versatilità del polimorfismo
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION