Skip to content

6. Interfejsy

Interfejs to struktura definiująca prototypy metod. Klasa implementująca interfejs musi zdefiniować kod wszystkich metod tego interfejsu.

6.1. Drzewo skryptów

Image

6.2. Pierwszy interfejs

Definiujemy interfejs [IExemple] w skrypcie [IExemple.php]. W tym samym skrypcie definiujemy dwie klasy implementujące interfejs [IExemple]:


<?php

// ścisłe przestrzeganie typów parametrów funkcji
declare(strict_types=1);

// przestrzeń nazw
namespace Exemples;

// interfejs
interface IExemple {
    // metody interfejsu
    public function ajouter(int $i, int $j): int;
    public function soustraire(int $i, int $j): int;
}

// implementacja 1 interfejsu 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;
    }

}

// implementacja 2 interfejsu 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;
    }

}

Komentarze

  • wiersze 10–14: interfejs IExemple definiuje dwie metody: funkcję ajouter (wiersz 12) oraz funkcję soustraire (wiersz 13). Interfejs nie definiuje kodu tych metod. Zrobią to klasy implementujące ten interfejs;
  • wiersz 17: klasa Classe1 implementuje interfejs IExemple. W związku z tym definiuje ona kod dla metod ajouter (wiersz 18) i soustraire (wiersz 22);
  • wiersze 29–38: to samo dotyczy klasy Classe2;

Korzystamy z interfejsu [IExemple] w poniższym skrypcie [interfaces-01.php]:


<?php

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

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

// ------------------- main
// utworzenie 2 obiektów typu Klasa1 i Klasa2
$c1 = new Classe1();
$c2 = new Classe2();
// wywołanie funkcji „oblicz”
calculer(4, 3, $c1);
calculer(14, 13, $c2);

Komentarze

  • wiersze 3–6: dołączenie i definicja klas oraz interfejsów wykorzystywanych przez skrypt;
  • wiersz 3: zamiast instrukcji include użyliśmy tutaj instrukcji require_once. Instrukcja include dołącza plik do bieżącego skryptu, nawet jeśli został on już wcześniej dołączony, podczas gdy instrukcja require_once dołącza go tylko raz. Dlatego w przypadku dołączania kodu preferowana jest instrukcja require_once;
  • wiersze 9–12: definiujemy funkcję;
  • wiersz 9: zadeklarowaliśmy tutaj typy trzech parametrów. Jeśli podczas wykonywania rzeczywisty parametr nie jest typu oczekiwanego, zgłaszany jest błąd. Trzeci parametr jest typu [IExemple]. Oznacza to, że rzeczywisty parametr musi być instancją klasy implementującej interfejs [IExemple], a więc w naszym przykładzie instancją klasy [Classe1] lub [Classe2];
  • wiersze 10–11: wykorzystujemy metody [ajouter, soustraire] z interfejsu [IExemple];
  • wiersz 19: trzeci parametr rzeczywisty jest typu [Classe1], zgodnego z typem [IExemple] trzeciego parametru formalnego funkcji;
  • wiersz 20: to samo dotyczy klasy [Classe2];

Wyniki

1
2
3
4
17
21
127
201

6.3. Wyprowadzanie interfejsu

Podobnie jak klasa potomna może rozszerzać klasę nadrzędną, tak samo interfejs potomny może rozszerzać interfejs nadrzędny.

Rozważmy interfejs [IExemple2] zdefiniowany w pliku [IExemple2.php]:


<?php

// ścisłe przestrzeganie typów parametrów funkcji
declare(strict_types=1);
// przestrzeń nazw
namespace Exemples;

// włączenie definicji interfejsu IExemple
require_once __DIR__."/IExemple.php";

// interfejs wywodzący się z IExemple
interface IExemple2 extends IExemple {
    // metoda interfejsu
    public function multiplier(int $i, int $j): int;
}

// implementacja interfejsu IExemple2
class Classe3 extends Classe2 implements IExemple2 {
   
    public function multiplier(int $a, int $b): int {
        return $a * $b;
    }

}

Uwagi

  • wiersz 6: interfejs [IExemple2] będzie znajdował się w tej samej przestrzeni nazw co interfejs [Iexemple];
  • wiersz 9: dołączamy plik definicji interfejsu [Iexemple];
  • wiersze 12–15: definiują interfejs [IExemple2], który rozszerza (dziedziczy) interfejs [Iexemple] (extends) i dodaje do niego nową metodę (wiersz 14);
  • wiersze 18–24: klasa [Classe3] rozszerza klasę [Classe2]. Ponieważ ta ostatnia implementuje interfejs [IExemple], to samo dotyczy klasy [Classe3]. W rzeczywistości klasa [Classe3] dziedziczy wszystkie metody publiczne i chronione klasy [Classe2], a więc również metody „dodaj” i „odejmij”. Ponadto wskazano, że klasa [Classe3] implementuje interfejs [IExemple2]. W związku z tym musi ona również implementować metodę „multiplier”, czego nie robi klasa [Classe2]. Odpowiadają za to wiersze 20–22;

Interfejs [IExemple2] jest wykorzystywany przez następujący skrypt [interfaces-02]:


<?php

// ścisłe przestrzeganie typów parametrów funkcji
declare(strict_types=1);

// dołączenie i kwalifikatory klas oraz interfejsów niezbędnych dla skryptu
require_once __DIR__."/Iexemple2.php";
use \Exemples\IExemple2;
use \Exemples\Classe3;

// funkcja
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
// utworzenie 1 obiektu typu Classe3, który implementuje IExemple2
$c3 = new Classe3();
// wywołanie funkcji „calculer”
calculer(4, 3, $c3);

Komentarze

  • wiersz 12: trzeci parametr funkcji [calculer] jest typu [IExemple2];
  • wiersze 13–15: wykorzystuje się trzy metody interfejsu [IExemple2];
  • wiersz 22: wywołuje się funkcję [calculer], podając jako trzeci parametr obiekt typu [IExemple2] (wiersz 20);

Wyniki

1
2
3
107
201
12

6.4. Przekazywanie interfejsu jako parametru funkcji

W poprzednim akapicie widzieliśmy, że gdy oczekiwanym typem parametru funkcji jest klasa, rzeczywistym parametrem może być obiekt typu oczekiwanego lub pochodnego. To samo dotyczy interfejsów, co pokazuje poniższy skrypt [interfaces-03.php]:


<?php

// ścisłe przestrzeganie typów parametrów funkcji
declare(strict_types=1);

// dołączenie i kwalifikacja klas oraz interfejsów niezbędnych dla skryptu
require_once __DIR__."/IExemple.php";
use \Exemples\IExemple;
require_once __DIR__."/IExemple2.php";
use \Exemples\Classe3;

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

// ------------------- main
// utworzenie 1 obiektu typu Classe3, który implementuje IExemple2, a tym samym IExemple
$c3 = new Classe3();
// wywołanie funkcji „oblicz”
calculer(4, 3, $c3);

Komentarze

  • w wierszu 13 funkcja [calculer] oczekuje jako trzeciego parametru typu [IExemple]. Oznacza to, że typ rzeczywistego parametru może być typu [IExemple] lub pochodnego;
  • wiersz 20: tworzona jest instancja obiektu $c3 typu [Classe3], klasy, która implementuje interfejs [IExemple2], który z kolei rozszerza interfejs [IExemple]. Ostatecznie więc $c3 implementuje interfejs [IExemple];
  • wiersz 22: wywoływana jest funkcja [calculer] z obiektem $c3 typu [Classe3] jako trzecim parametrem. Dzięki dziedziczeniu klas i implementacjom widzieliśmy, że obiekt $c3 implementuje typ [IExemple]. Można go zatem użyć jako trzeciego parametru;

Wyniki

107
201