Skip to content

6. Le interfacce

Un'interfaccia è una struttura che definisce i prototipi dei metodi. Una classe che implementa un'interfaccia deve definire il codice di tutti i metodi dell'interfaccia.

6.1. L’albero degli script

Image

6.2. Una prima interfaccia

Definiamo un'interfaccia [IExemple] in uno script [IExemple.php]. Nello stesso script, definiamo due classi che implementano l'interfaccia [IExemple]:


<?php

// rigoroso rispetto dei tipi dei parametri delle funzioni
declare(strict_types=1);

// spazio dei nomi
namespace Exemples;

// un'interfaccia
interface IExemple {
    // i metodi dell'interfaccia
    public function ajouter(int $i, int $j): int;
    public function soustraire(int $i, int $j): int;
}

// implementazione 1 dell'interfaccia IExemple
class Classe1 implements IExemple {
    public function ajouter(int $a, int $b): int {
        return $a + $b + 10;
    }

    public function soustraire(int $a, int $b): int {
        return $a - $b + 20;
    }

}

// implementazione 2 dell'interfaccia IExemple
class Classe2 implements IExemple {
    public function ajouter(int $a, int $b) : int{
        return $a + $b + 100;
    }

    public function soustraire(int $a, int $b) : int {
        return $a - $b + 200;
    }

}

Commenti

  • righe 10-14: l'interfaccia IExemple definisce due metodi: la funzione ajouter (riga 12) e la funzione soustraire (riga 13). L'interfaccia non definisce il codice di questi metodi. Saranno le classi che implementano l'interfaccia a farlo;
  • riga 17: la classe Classe1 implementa l'interfaccia IExemple. Definisce quindi un codice per i metodi ajouter (riga 18) e soustraire (riga 22);
  • righe 29-38: lo stesso vale per la classe Classe2;

Utilizziamo l’interfaccia [IExemple] nel seguente script [interfaces-01.php]:


<?php

require_once "IExemple.php";
use \Exemples\Classe1;
use \Exemples\Classe2;
use \Exemples\IExemple;

// funzione
function calculer(int $a, int $b, IExemple $interface) : void {
  print $interface->ajouter($a, $b)."\n";
  print $interface->soustraire($a, $b)."\n";
}

// ------------------- main
// creazione di 2 oggetti di tipo Classe1 e Classe2
$c1 = new Classe1();
$c2 = new Classe2();
// chiamata della funzione «calcolare»
calculer(4, 3, $c1);
calculer(14, 13, $c2);

Commenti

  • righe 3-6: inclusione e definizione delle classi e delle interfacce utilizzate dallo script;
  • riga 3: al posto dell'istruzione include, qui abbiamo utilizzato l'istruzione require_once. L'istruzione include inserisce un file nello script corrente anche se è già stato incluso, mentre l'istruzione require_once lo include una sola volta. Pertanto, per quanto riguarda l'inclusione di codice, è preferibile utilizzare l'istruzione require_once;
  • righe 9-12: si definisce una funzione;
  • riga 9: qui abbiamo dichiarato il tipo dei tre parametri. Se durante l’esecuzione il parametro effettivo non è del tipo previsto, viene generato un errore. Il terzo parametro è di tipo [IExemple]. Ciò significa che il parametro effettivo deve essere un’istanza di una classe che implementi l’interfaccia [IExemple], quindi, nel nostro esempio, un’istanza delle classi [Classe1] o [Classe2];
  • righe 10-11: utilizziamo i metodi [ajouter, soustraire] dell’interfaccia [IExemple];
  • riga 19: il terzo parametro effettivo è di tipo [Classe1], compatibile con il tipo [IExemple] del terzo parametro formale della funzione;
  • riga 20: lo stesso vale per la classe [Classe2];

Risultati

1
2
3
4
17
21
127
201

6.3. Derivazione di un'interfaccia

Così come una classe figlia può estendere una classe padre, un'interfaccia figlia può estendere un'interfaccia padre.

Consideriamo l’interfaccia [IExemple2] definita nel file [IExemple2.php]:


<?php

// rigoroso rispetto dei tipi dei parametri delle funzioni
declare(strict_types=1);
// spazio dei nomi
namespace Exemples;

// inclusione della definizione dell'interfaccia IExemple
require_once __DIR__."/IExemple.php";

// un'interfaccia che deriva da IExemple
interface IExemple2 extends IExemple {
    // il metodo dell'interfaccia
    public function multiplier(int $i, int $j): int;
}

// implementazione dell'interfaccia IExemple2
class Classe3 extends Classe2 implements IExemple2 {
   
    public function multiplier(int $a, int $b): int {
        return $a * $b;
    }

}

Commenti

  • riga 6: l'interfaccia [IExemple2] si troverà nello stesso spazio dei nomi dell'interfaccia [Iexemple];
  • riga 9: si include il file di definizione dell’interfaccia [Iexemple];
  • righe 12-15: definiscono l'interfaccia [IExemple2] che estende (eredita) l'interfaccia [Iexemple] e aggiunge un nuovo metodo (riga 14);
  • righe 18-24: la classe [Classe3] estende la classe [Classe2]. Poiché quest’ultima implementa l’interfaccia [IExemple], lo stesso vale anche per la classe [Classe3]. Infatti, [Classe3] eredita tutti i metodi pubblici e protetti della classe [Classe2], quindi i metodi «aggiungi» e «sottrai». Inoltre, si indica che [Classe3] implementa l’interfaccia [IExemple2]. In quanto tale, deve implementare anche il metodo «moltiplicare», cosa che [Classe2] non fa. È ciò che fanno le righe 20-22;

L’interfaccia [IExemple2] viene utilizzata dallo script [interfaces-02] seguente:


<?php

// rigoroso rispetto dei tipi dei parametri delle funzioni
declare(strict_types=1);

// inclusione e qualifiche delle classi e delle interfacce necessarie allo script
require_once __DIR__."/Iexemple2.php";
use \Exemples\IExemple2;
use \Exemples\Classe3;

// funzione
function calculer(int $a, int $b, IExemple2 $interface) {
    print $interface->ajouter($a, $b) . "\n";
    print $interface->soustraire($a, $b) . "\n";
    print $interface->multiplier($a, $b) . "\n";
}

// ------------------- main
// creazione di 1 oggetto di tipo Classe3 che implementa IExemple2
$c3 = new Classe3();
// chiamata della funzione calcolare
calculer(4, 3, $c3);

Commenti

  • riga 12: il terzo parametro della funzione [calculer] è di tipo [IExemple2];
  • righe 13-15: si utilizzano i tre metodi dell'interfaccia [IExemple2];
  • riga 22: si chiama la funzione [calculer] con come terzo parametro un oggetto di tipo [IExemple2] (riga 20);

Risultati

1
2
3
107
201
12

6.4. Passaggio di un’interfaccia come parametro di una funzione

Abbiamo visto nel paragrafo precedente che quando il tipo previsto per un parametro di funzione è una classe, il parametro effettivo può essere un oggetto del tipo previsto o derivato. Lo stesso vale per le interfacce, come mostra il seguente script [interfaces-03.php]:


<?php

// rigoroso rispetto dei tipi dei parametri delle funzioni
declare(strict_types=1);

// inclusione e qualificazione delle classi e delle interfacce necessarie allo script
require_once __DIR__."/IExemple.php";
use \Exemples\IExemple;
require_once __DIR__."/IExemple2.php";
use \Exemples\Classe3;

// funzione
function calculer(int $a, int $b, IExemple $interface) {
  print $interface->ajouter($a, $b) . "\n";
  print $interface->soustraire($a, $b) . "\n";
}

// ------------------- main
// creazione di 1 oggetto di tipo Classe3 che implementa IExemple2 e quindi IExemple
$c3 = new Classe3();
// chiamata della funzione calcolare
calculer(4, 3, $c3);

Commenti

  • riga 13: la funzione [calculer] richiede come terzo parametro un tipo [IExemple]. Ciò significa che il tipo del parametro effettivo potrà essere di tipo [IExemple] o derivato;
  • riga 20: si istanzia un oggetto $c3 di tipo [Classe3], classe che implementa l’interfaccia [IExemple2], la quale a sua volta estende l’interfaccia [IExemple]. Quindi, in definitiva, $c3 implementa l’interfaccia [IExemple];
  • riga 22: si chiama la funzione [calculer] con come terzo parametro un oggetto $c3 di tipo [Classe3]. Grazie al meccanismo di ereditarietà delle classi e alle implementazioni, abbiamo visto che l’oggetto $c3 implementava il tipo [IExemple]. È quindi possibile utilizzarlo come terzo parametro;

Risultati

107
201