anton_reut
@anton_reut
Начинающий веб-разработчик

Правильно ли я понимаю суть Интерфейсов в ООП?

Читаю книгу Мэт Зандстра "PHP. Объекты, шаблоны и методики программирования" (5-е издание 2019 год), дошел до раздела про Интерфейсы и конкретно так встрял на стадии понимания как это работает и зачем, тем более что этой вещи там уделяется 4 страницы (по крайней мере основам), мало что понял, перечитал раза три и полез в интернет копать альтернативные варианты разъяснений. Исходя из описания Мэта я понял что простой фразы "интерфейсы обязуют реализовать все перечисленные методы" - недостаточно, там скрывается что-то более сложное, полезное и интересное.
В принципе теперь то я понимаю что имел в виду Мэт но вот так сходу было тяжеловато, итак что у меня получилось накодить для Понимания интерфейсов (и как я понял Суть Интерфейсов):

<?php

// Интерфейс - включить музыку.
interface PlayMusic{
	
	public function PlayMusic();
	
}

// Реализация метода Включить музыку - через CD-диск
class CdPlayer implements PlayMusic{
	
	private $cd;
	
	public function PlayMusic($cd){
		$this->cd = $cd;
		$this->cd->play();
	}
}

// Реализация метода Включить музыку - через радио
class RadioPlayer implements PlayMusic{
	
	private $fm;
	
	public function PlayMusic($fm){
		$this->fm = $fm;
		$this->fm->play();
	}
}

// Класс-клиент принимающий в конструкторе класс-сервис и использующий реализованные в классе-сервисе методы.
class MusicCenter{
	
	public $player;
	
	public function __construct(PlayMusic $player){
		$this->player = $player;
	}
}


// Создание класса-сервиса $player - проигрывать диск.
$player_1 = new CdPlayer('cd1');

// Создание класса-сервиса $player - включить радио.
$player_2 = new RadioPlayer('105.9');


//Создание класса-клиента $musiccenter который проигрывает диск.
$musicCenter_1 = new MusicCenter($player_1);
//Вызов метода класса-сервиса в классе-клиенте
$musicCenter_1->$this->player->Playmusic();


//Создание класса-клиента $musiccenter который включает радио.
$musicCenter_2 = new MusicCenter($player_2);
//Вызов метода класса-сервиса в классе-клиенте
$musicCenter_2->$this->player->Playmusic();

?>


БАЗОВЫЕ понятия для закрепления Понимания (я реально записал это конспектом, для себя):

Класс-клиент не знает как именно включается музыка, ему важно включить музыку, для этого ему передается класс-сервис который реализует это действие по-своему (например нажимаем определенную кнопку на Муз.центре).

Структура действия: Класс-клиент -> Класс-сервис -> реализованный метод.

Класс клиент не может принять класс-сервис если он не относится к Типу определенному Интерфейсом.
(например мы не можем включить музыку нажав кнопку включения питания либо какую то другую не реализующую метод включения музыки, например кнопку "открыть лоток с дисками").


Создание интерфейса приводит к созданию нового Типа класса в соответствии с именем Интерфейса (из книги Мэта Зандстры).
В результате мы имеем 4 типа Класса:
1 тип - PlayMusic - он же Интерфейс.
2 тип - CdPlayer - сервис
3 тип - RadioPlayer - сервис
4 тип - MusicCenter - клиент

Можете конечно поржать но я даже вот такое рисовал "на салфетке" пока обдумывал всё и пытался понять :)

5d6c327112288129385866.jpeg

И финальный вопрос - ЗАЧЕМ делать интерфейсы? Всего лишь отделить в очередной раз логику от реализации?
  • Вопрос задан
  • 323 просмотра
Пригласить эксперта
Ответы на вопрос 3
coderisimo
@coderisimo
Интерфейсы это как контракты. Т.е если класс реализует интерфейс, значит он исполняет наш контракт. Это удобно! Например, есть класс TaxCollector , он принимает класс реализующий интерфейс moneySource . Допустим у нас есть сейчас только два класса (Businessman , Criminal), которые мы можем передавать в TaxCollector и каждый реализует getMoney. Далее мы можем добавить еще дюжину и наш TaxCollector будет также хорошо работать с ними. Для него ничего не изменится. А все потому, что интерфейс moneySource строго определен и включает метод getMoney. Остальное не важно. Любой из классов - Student, Man, Woman, Professor, Gigolo , которые поддерживают данный интерфейс годятся для работы с TaxCollector. Это гарантируется интерфейсом moneySource.
Ответ написан
Maksclub
@Maksclub
maksfedorov.ru
Интерфейс — основа полиморфизма.
Есть один интерфейс, с ним умеет работать некий код. Вуаля, любая реализация этого интерфейса подходит для этого самого кода.

Пример: есть некий класс заказа Order, он работает с неким отправителем Sender, мы можем жестко "научить" его работать с ним в коде этого Order, а можем просто передавать ему отправителя через конструктор (привет DI), но передавать не сам класс/реализацию, а лишь его абстракцию — интерфейс, то, что будет отправлять, но как — не важно, это будет SenderInterface.

Теперь мы в Order можем юзать полиморфизм: использовать любой Sender, который реализует этот самый SenderInterface

Код:
interface SenderInterface
{
     public function send(): void {};
}

class Order
{
     // тут будет любой, но обязан реализовать SenderInterface
     private $sender;

     public function __constuctor(SenderInterface $sender) {
          $this->sender = $sender;
     }
 
     public function save(): void 
    {
        // какой в приватном поле сидит, тот и отправит, 
        // а кто -- текущему объекту все равно, это есть инверсия зависимости 
        // и принцип единственной ответственности, данный класс не отправляет, а поручает
        $this->sender->send();
    }
}

class MailSender implement SenderInterface
{
     public function send(): void
     {
           // тут отправка почтой
     }
}

class TelegramSender implement SenderInterface
{
     public function send(): void
     {
           // тут отправка телегой
     }
}


// Пример полиморфной работы Order
$order = new Order(new TelegramSender());
$order->save(); // тут отправка телеграмом

$order = new Order(new MailSender());
$order->save(); // тут отправка mail


Как видите, мы можем добавлять новые сендеры, в них что-то делать, а с ордером будем работать как прежде $order->save() , и вообще его код не трогать

Дополнение
Интерфейс есть у любого класса (публичны/приватный), интерфейс наследуется у абстрактного/обычного класса ребенком (кроме того, что наследует поведение), почему полиморфизм иногда (на самом деле ооочень часто) объясняют именно на наследовании одного класса и множественной реализации в виде детей. На самом деле все дело в этом самом интерфейсе.
Ответ написан
@stictt
просто рак
Не скажу за тонкости PHP , но в целом направление верное. Прежде всего интерфейс выполняет 2 роли, это интерфейс взаимодействия класса и апкастинг. Интерфейс взаимодействия это все публичные члены и методы класса в своей совокупности к которым можно обратиться, интерфейс структуризирует для всех классов один интерфейс взаимодействия, как человек выше говорил, по договору. Но это только малая часть преимуществ, основная сила это Апкаст, приведение от частного к общему. Грубо говоря вы назначили реализацию интерфейса классу, потом вы можете привести класс к интерфейсу, и обьявить тип интерфейса присвоив в переменную любой класс который будет иметь реализацию интерфейса, таким образом мы получаем очень динамическую систему которая может менять реализацию, манеру поведения, и много чего еще, без изменения в коде, по одному только интерфейсу взаимодействия. У вас 100500 классов, а обращаться вы к ним можете абсолютно одинаково, вам не нужно прописывать для каждого типа. для каждого другого обьекта вызов сугубо его методов.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы