Прилагане на интерфейси в ООП в PHP

И така, вече установихме, че интерфейсите са добър начин за контрол на това, че всички необходими методи на класа са реализирани.

Нека разгледаме още един, по-практичен, пример. Нека имаме клас, който ще съхранява масив от обекти-фигури:

FiguresCollection.php<?php
  class FiguresCollection
  {
    private $figures = []; // масив за фигури
  }
?>

Нека реализираме в нашия клас метод addFigure за добавяне на обекти в колекцията:

FiguresCollection.php<?php
  class FiguresCollection
  {
    private $figures = [];
    
    // Параметър се подава обект с фигура:
    public function addFigure($figure)
    {
      $this->figures[] = $figure;
    }
  }
?>

Очевидно е, че разчитаме на това, че параметър на метода addFigure ще бъде подаден обект с фигура. Но за това няма никакъв контрол!

Нека използваме подсказка за типове и изрично посочим типа на обектите като Figure:

FiguresCollection.php<?php
  class FiguresCollection
  {
    private $figures = [];
    
    public function addFigure(Figure $figure)
    {
      $this->figures[] = $figure;
    }
  }
?>

Нека разберем какво направихме.

Ако Figure беше реално съществуващ клас, то в параметъра на метода бихме могли да подадем обекти от този клас, както и от неговите наследници.

При нас обаче Figure е интерфейс. В този случай подсказката означава, че параметър на метода могат да бъдат подавани само обекти от класове, които реализират нашия интерфейс.

Нека опитаме да създадем обект от нашия клас и да добавим в него фигури:

index.php<?php
  $figuresCollection = new FiguresCollection;
  
  // Ще добавим няколко квадрата:
  $figuresCollection->add(new Quadrate(2));
  $figuresCollection->add(new Quadrate(3));
  
  // Ще добавим няколко правоъгълника:
  $figuresCollection->add(new Rectangle(2, 3));
  $figuresCollection->add(new Rectangle(3, 4));
?>

Опитът да се добави обект от някой друг клас ще доведе до грешка:

index.php<?php
  $figuresCollection = new FiguresCollection;
  
  class Test {}; // някакъв друг клас
  $figuresCollection->add(new Test); // ще даде грешка
?>

Какво на практика ни дава такъв контрол: тъй като всички фигури, добавени в колекцията, реализират интерфейса Figure, можем да сме сигурни, че всяка от тях ще има метод getSquare и метод getPerimeter.

Възможно е в бъдеще освен квадрат и правоъгълник да се появи, например, и триъгълник. В този случай и триъгълникът ще има методи getSquare и getPerimeter.

На практика това ни дава следното: можем в класа FiguresCollection да направим, например, метод getTotalSquare, намиращ общата площ на фигурите в колекцията. В този метод ще обхождаме в цикъл масива от фигури и за всяка фигура ще извикваме метода getSquare.

Тъй като всяка фигура реализира интерфейса Figure, можем да сме на 100% сигурни в това, че всяка фигура ще има този метод getSquare.

И така, ето реализацията на метода:

php<?php
  class FiguresCollection
  {
    private $figures = [];
    
    public function addFigure(Figure $figure)
    {
      $this->figures[] = $figure;
    }
    
    // Ще намерим общата площ:
    public function getTotalSquare()
    {
      $sum = 0;
      
      foreach ($this->figures as $figure) {
        $sum += $figure->getSquare(); // използваме метода getSquare
      }
      
      return $sum;
    }
  }
?>
Задание 1

Без да надничате в моя код, реализирайте същия клас FiguresCollection.

Задание 2

Добавете в класа FiguresCollection метод getTotalPerimeter за намиране на общия периметър на всички фигури.