back-end php Intermediate 3 min

Interfacce e Trait in PHP: quando usare quale

interface implements, Trait per codice riutilizzabile, conflitti tra trait

· 583 parole

Nella pillola sull'ereditarietà abbiamo visto che una classe PHP può estendere con extends solo una classe alla volta. Interfacce e trait risolvono due limiti diversi di questa regola: le prime permettono di rispettare più contratti contemporaneamente; i secondi permettono di condividere codice concreto tra classi che non hanno nulla in comune nella loro gerarchia.

Un'interfaccia è come un certificato di conformità: dice «questo prodotto rispetta lo standard X», senza dire come sia stato costruito. Un trait è più simile a un componente prefabbricato: lo stesso pezzo, letteralmente identico, viene montato in prodotti anche molto diversi tra loro.

1. interface: il contratto, senza implementazione

Un'interfaccia dichiara solo le firme dei metodi, mai il loro corpo. Il manuale è netto: tutti i metodi di un'interfaccia sono astratti per natura, e tutti pubblici. Una classe può implementare più interfacce insieme, separate da virgola: qui non c'è il limite dell'ereditarietà singola.

interface Loggabile {
    public function log(string $messaggio): void;
}

interface Serializzabile {
    public function toArray(): array;
}

class Ordine implements Loggabile, Serializzabile {
    public function log(string $messaggio): void { /* ... */ }
    public function toArray(): array { /* ... */ }
}

Regola sintattica dal manuale: se una classe estende e implementa allo stesso tempo, extends deve comparire prima di implements.

class Base { /* ... */ }

class Derivata extends Base implements Loggabile {
    // extends prima, implements dopo: ordine obbligatorio
}

Fonte: PHP Manual, «Object Interfaces»: tutti i metodi in un'interfaccia devono essere public; una classe può implementare più interfacce; extends deve precedere implements.

2. Trait: codice condiviso, non un vero contratto

Un trait contiene implementazione vera, metodi con corpo, non solo firme, pensata per essere mescolata dentro una o più classi. Il manuale usa proprio questa parola: un trait non è ereditato, ma incluso o «mescolato» (mixed in), diventando di fatto parte della classe stessa.

trait Timestampabile {
    protected ?\DateTimeImmutable $creatoIl = null;

    public function segnaCreazione(): void {
        $this->creatoIl = new \DateTimeImmutable();
    }
}

class Articolo {
    use Timestampabile;
}

class Commento {
    use Timestampabile;  // stesso codice, classi senza relazione tra loro
}

Fonte: PHP Manual, «Traits»: un trait non è ereditato, ma incluso o mescolato, diventando parte della classe che lo usa.

3. Conflitti tra trait: insteadof e as

Se due trait usati nella stessa classe hanno un metodo con lo stesso nome, PHP non sceglie da solo: genera un errore fatale finché non risolvi tu il conflitto esplicitamente, con insteadof (scegli quale versione tenere) e, se serve, as (dai un alias anche all'altra).

trait Cane { public function saluta() { echo 'Bau'; } }
trait Gatto { public function saluta() { echo 'Miao'; } }

class AnimaleBuffo {
    use Cane, Gatto {
        Cane::saluta insteadof Gatto;   // scegli la versione di Cane
        Gatto::saluta as miagola;        // ma tieni anche l'altra, con un altro nome
    }
}

$a = new AnimaleBuffo();
$a->saluta();   // 'Bau'
$a->miagola();  // 'Miao'

Fonte: PHP Manual, «Traits»: per risolvere conflitti tra trait usati nella stessa classe si usa insteadof per scegliere un metodo, e as per assegnare un alias a quello escluso.

4. Quando usare quale

Usa un'interface quando ti interessa il contratto: «qualunque cosa sia, deve saper fare X», utile per scrivere codice che lavora con oggetti intercambiabili, senza sapere la loro classe esatta.

Usa un trait quando ti interessa il codice: «questa logica concreta serve identica in classi che non hanno nessuna relazione gerarchica tra loro».

Sono spesso usati insieme: un'interfaccia dichiara il contratto, un trait fornisce un'implementazione di default riusabile, e la classe dichiara implements per il contratto e use per prendersi l'implementazione già pronta.

Ordine implementa Loggabile e Serializzabile, il trait Timestampabile è usato da Articolo e Commento, un conflitto tra trait risolto con insteadof e as

In sintesi

La domanda giusta non è «interfaccia o trait?» come se fossero alternative, ma «sto descrivendo cosa un oggetto deve fare, o sto riusando codice che già sa farlo?». La risposta a quella domanda sceglie lo strumento al posto tuo.

Accedi o registrati per votare, salvare o commentare la pillola

Commenti (0)

Registrati per commentare, fare domande e proporre l'argomento della prossima pillola (oppure accedi se hai già un account).

Nessun commento. Sii il primo a scrivere qualcosa.

Pillole correlate