Utente:LoStrangolatore/corsojava/ereditarietà/2

Nella lezione scorsa siamo rimasti al problema del CreatoreDiFiltri. Supponiamo di voler usare un filtro più intelligente del FiltroContenutoNonAmmesso, ad esempio il FiltroBlackList:

public class CreatoreDiFiltri {
 
    public FiltroBlackList creaFiltro() {
        ... // legge black list da file
        return new FiltroBlackList(listaMittentiVietati);
    }
 
}

Però ora il MailChecker non compila più. Il compilatore si lamenta, perché l'istruzione FiltroContenutoNonAmmesso filtro = factory.creaFiltro(); si aspetta ancora che il CreatoreDiFiltri restituisca un FiltroContenutoNonAmmesso. Quindi dobbiamo modificare anche questo programma, e più in generale tutti i programmi che usano il CreatoreDiFiltri.

Il problema vero è che il CreatoreDiFiltri non dice ai clients: Il metodo creaFiltro() restituisce un filtro, ma dice invece: Il metodo creaFiltro() restituisce un FiltroBlackList. Al MailChecker non interessa quale filtro stiamo utilizzando, perché il metodo èSpam è uguale per tutti. Tuttavia, si "adatta" a questa pecca del CreatoreDiFiltri, e quindi dice: Che sia proprio un FiltroBlackList non mi interessa nel concreto, però, visto che è così, ne prendo atto e uso una variabile di quel tipo specifico (che è incompatibile con altri tipi).

Inoltre, non è bello che i filtri siano del tutto indipendenti tra loro. Il fatto che i filtri abbiano tutti la stessa interfaccia è solo un caso, nel senso che il compilatore non lo sa, e nessuno ci impedisce di scrivere un filtro con un metodo public boolean èpSpam a causa di un errore di battitura. Oppure, il programmatore di un altro progetto software, che non ha partecipato allo sviluppo del programma, ma ne riutilizza le classi, potrebbe estendere il set di filtri aggiungendone uno con un metodo public boolean controlla(Mail mail) perché non sa che ci siamo autoimposti la regola (non scritta) che tutti filtri devono avere la stessa interfaccia. In entrambi i casi, la nuova classe sarebbe sintatticamente corretta di per sé, però sarebbe incompatibile con la classe MailChecker, la quale si aspetta di trovare un metodo di nome èSpam che non c'è. Quindi, il compilatore segnala un errore nella classe MailChecker stessa e non in queste nuove classi, anche se l'errore vero e proprio sta in queste ultime.

Soluzione

Sarebbe bello se il MailChecker potesse dichiarare una variabile con un tipo astratto Filtro che rappresenta un generico filtro.

Java permette di farlo. Dobbiamo definire un tipo Filtro:

public interface Filtro {
    public boolean controlla(Mail m);
}

Questo tipo definisce l'interfaccia che deve essere comune a tutti gli oggetti che vogliono essere considerati filtri per le mail. Quindi modifichiamo le classi filtro che abbiamo scritto aggiungendo la clausola implements Filtro. Ad esempio:

public class FiltroBlackList implements Filtro {
        ... // campi privati, costruttore ecc.
 
        public boolean èSpam(Mail m) { ... }
}
public class CreatoreDiFiltri {
 
    public Filtro creaFiltro() {
        ... // legge black list da file
        return new FiltroBlackList(listaMittentiVietati);
    }
 
}
public class MailChecker {
    public static void main(String[] args) {
        // Passo 1: creo un canale verso il terminale
        // TODO BufferedReader attorno a System.in
 
        CreatoreDiFiltri factory = new CreatoreDiFiltri();
 
        // Passo 1: ottengo i dati di una e-mail e creo un oggetto Mail
        Mail mail = Mail.leggiDaTerminale();
 
        // Passo 2: analizzo i dati della e-mail tramite un filtro
        Filtro filtro = factory.creaFiltro();
        boolean spam = filtro.èSpam(mail);
 
        // Passo 3: restituisco risultato all'utente
        if (spam)
            System.out.println("La e-mail è probabilmente spam");
        else
            System.out.println("La e-mail probabilmente non è spam");
 
    }
 
}