Skip to content

6. Інтерфейси

Інтерфейс — це структура, що визначає прототипи методів. Клас, який реалізує інтерфейс, повинен визначити код усіх методів цього інтерфейсу.

6.1. Дерево скриптів

Image

6.2. Перший інтерфейс

Ми визначаємо інтерфейс [IExemple] у скрипті [IExemple.php]. У цьому ж скрипті ми визначаємо два класи, що реалізують інтерфейс [IExemple]:


<?php

// суворе дотримання типів параметрів функцій
declare(strict_types=1);

// простір імен
namespace Exemples;

// інтерфейс
interface IExemple {
    // методи інтерфейсу
    public function ajouter(int $i, int $j): int;
    public function soustraire(int $i, int $j): int;
}

// реалізація 1 інтерфейсу 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;
    }

}

// реалізація 2 інтерфейсу 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;
    }

}

Коментарі

  • рядки 10–14: інтерфейс IExemple визначає два методи: функцію ajouter (рядок 12) та функцію soustraire (рядок 13). Інтерфейс не визначає код цих методів. Це роблять класи, що реалізують інтерфейс;
  • рядок 17: клас Classe1 реалізує (implements) інтерфейс IExemple. Отже, він визначає код для методів ajouter (рядок 18) та soustraire (рядок 22);
  • рядки 29–38: те саме стосується класу Classe2;

Ми використовуємо інтерфейс [IExemple] у наступному скрипті [interfaces-01.php]:


<?php

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

// функція
function calculer(int $a, int $b, IExemple $interface) : void {
  print $interface->ajouter($a, $b)."\n";
  print $interface->soustraire($a, $b)."\n";
}

// ------------------- main
// створення 2 об’єктів типів Classe1 та Classe2
$c1 = new Classe1();
$c2 = new Classe2();
// виклик функції «calculer»
calculer(4, 3, $c1);
calculer(14, 13, $c2);

Коментарі

  • рядки 3–6: включення та визначення класів та інтерфейсів, що використовуються скриптом;
  • рядок 3: замість оператора include тут використано оператор require_once. Інструкція include включає файл у поточний скрипт, навіть якщо він уже був включений, тоді як інструкція require_once включає його лише один раз. Тому щодо включення коду краще використовувати інструкцію require_once;
  • рядки 9–12: визначаємо функцію;
  • рядок 9: тут ми оголосили типи трьох параметрів. Якщо під час виконання фактичний параметр не відповідає очікуваному типу, генерується помилка. Третій параметр має тип [IExemple]. Це означає, що фактичний параметр має бути екземпляром класу, що реалізує інтерфейс [IExemple], отже, у нашому прикладі — екземпляром класів [Classe1] або [Classe2];
  • рядки 10–11: ми використовуємо методи [ajouter, soustraire] інтерфейсу [IExemple];
  • рядок 19: третій фактичний параметр має тип [Classe1], сумісний із типом [IExemple] третього формального параметра функції;
  • рядок 20: те саме стосується класу [Classe2];

Результати

1
2
3
4
17
21
127
201

6.3. Похідний інтерфейс

Так само, як дочірній клас може успадковувати батьківський клас, дочірній інтерфейс може успадковувати батьківський інтерфейс.

Розглянемо інтерфейс [IExemple2], визначений у файлі [IExemple2.php]:


<?php

// суворе дотримання типів параметрів функцій
declare(strict_types=1);
// простір імен
namespace Exemples;

// включення визначення інтерфейсу IExemple
require_once __DIR__."/IExemple.php";

// інтерфейс, що походить від IExemple
interface IExemple2 extends IExemple {
    // метод інтерфейсу
    public function multiplier(int $i, int $j): int;
}

// реалізація інтерфейсу IExemple2
class Classe3 extends Classe2 implements IExemple2 {
   
    public function multiplier(int $a, int $b): int {
        return $a * $b;
    }

}

Коментарі

  • рядок 6: інтерфейс [IExemple2] буде в тому самому просторі імен, що й інтерфейс [Iexemple];
  • рядок 9: включено файл визначення інтерфейсу [Iexemple];
  • рядки 12–15: визначають інтерфейс [IExemple2], який розширює (успадковує) інтерфейс [Iexemple] (extends) та додає до нього новий метод (рядок 14);
  • рядки 18–24: клас [Classe3] розширює клас [Classe2]. Оскільки останній реалізує інтерфейс [IExemple], це також стосуватиметься класу [Classe3]. Дійсно, [Classe3] успадковує всі публічні та захищені методи класу [Classe2], а отже, і методи додавання та віднімання. Крім того, зазначено, що [Classe3] реалізує інтерфейс [IExemple2]. Відповідно, він також повинен реалізовувати метод «multiplier», чого не робить [Classe2]. Саме це роблять рядки 20–22;

Інтерфейс [IExemple2] використовується наступним скриптом [interfaces-02]:


<?php

// суворе дотримання типів параметрів функцій
declare(strict_types=1);

// імпорт та кваліфікація класів та інтерфейсів, необхідних для скрипта
require_once __DIR__."/Iexemple2.php";
use \Exemples\IExemple2;
use \Exemples\Classe3;

// функція
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
// створення 1 об’єкта типу Classe3, який реалізує IExemple2
$c3 = new Classe3();
// виклик функції «calculer»
calculer(4, 3, $c3);

Коментарі

  • рядок 12: третій параметр функції [calculer] має тип [IExemple2];
  • рядки 13–15: використовуються три методи інтерфейсу [IExemple2];
  • рядок 22: викликається функція [calculer], де третім параметром є об’єкт типу [IExemple2] (рядок 20);

Результати

1
2
3
107
201
12

6.4. Передача інтерфейсу як параметра функції

У попередньому розділі ми бачили, що коли очікуваний тип для параметра функції є класом, то фактичним параметром може бути об’єкт очікуваного типу або його похідного. Те саме стосується інтерфейсів, як показано в наступному скрипті [interfaces-03.php]:


<?php

// суворе дотримання типів параметрів функцій
declare(strict_types=1);

// імпорт та кваліфікація класів та інтерфейсів, необхідних для скрипта
require_once __DIR__."/IExemple.php";
use \Exemples\IExemple;
require_once __DIR__."/IExemple2.php";
use \Exemples\Classe3;

// функція
function calculer(int $a, int $b, IExemple $interface) {
  print $interface->ajouter($a, $b) . "\n";
  print $interface->soustraire($a, $b) . "\n";
}

// ------------------- main
// створення 1 об’єкта типу Classe3, який реалізує IExemple2, а отже, і IExemple
$c3 = new Classe3();
// виклик функції «calculer»
calculer(4, 3, $c3);

Коментарі

  • у рядку 13 функція [calculer] очікує як третій параметр тип [IExemple]. Це означає, що тип фактичного параметра може бути типом [IExemple] або похідним від нього;
  • рядок 20: створюється екземпляр об’єкта $c3 типу [Classe3], клас якого реалізує інтерфейс [IExemple2], який, у свою чергу, розширює інтерфейс [IExemple]. Отже, зрештою $c3 реалізує інтерфейс [IExemple];
  • рядок 22: викликається функція [calculer], третім параметром якої є об’єкт $c3 типу [Classe3]. Завдяки взаємодії успадкування класів та реалізацій ми побачили, що об’єкт $c3 реалізує тип [IExemple]. Отже, його можна використовувати як третій параметр;

Результати

107
201