Сначала определение из википедии - способность функции обрабатывать данные разных типов.
PHP - язык с динамической и слабой типизацией, это значит, что в процессе работы программы тип переменной может быть изменен. Более того, операнды не обязаны быть одного типа и интерпретатор сможет обработать такие варианты операций без ошибок:
<?php
echo 1 . 1; // 11
echo PHP_EOL;
echo 1 + 1; // 2
echo PHP_EOL;
echo 1 + '1'; // 2
?>
Такое поведение уже формально подпадает под определение, но мы конечно хотим от полиморфизма совсем другого.
Практическая польза от полиморфизма в том, чтобы использовать один и тот же код для обработки данных полученных из разных функций, классов файлов и других источников.
Например, у нас есть новостной сайт. И мы получаем из базы данных информацию о новостях, чтобы вывести их пользователю. Чтобы не подключать бд к php песочнице, определим формат новости как json объект:
{
"title": "This is title",
"text": "This is text",
"time": "2024-01-13 12:00:00"
}
Напишем функцию, которая разберет этот json на отдельные поля и выведет на экран с нужным форматированием.
<?php
$jsonString = '{"title": "This is title", "text": "This is text", "time": "2024-01-13 12:00:00"}';
function publish($jsonString){
$jsonObject = json_decode($jsonString);
echo "<h1>$jsonObject->title</h1>";
echo PHP_EOL;
echo "<p>$jsonObject->text</p>";
echo PHP_EOL;
echo "<span>$jsonObject->time</span>";
}
publish($jsonString);
?>
Пока что неплохо справляемся и без ооп с полиморфизмом. Давайте добавим на наш сайт не только новости, но еще и статьи. В статьях не будет времени публикации, но будет имя автора. К сожалению, наша функция перестанет работать, если мы передадим такой json в нее. И тогда надо писать другую. И так для каждого типа контента. Конечно можно добавить несколько if - else и перерисовать дизайн блоков в зависимости от типов, но есть способ получше.
Теперь нам придется написать несколько классов.
<?php
class News
{
private $jsonString;
public function __construct()
{
$this->jsonString = '{"title": "This is title", "text": "This is text", "time": "2024-01-13 12:00:00"}';
}
public function publish()
{
$jsonObject = json_decode($this->jsonString);
echo "<div class='news'>";
echo "<h1>$jsonObject->title</h1>";
echo "<p>$jsonObject->text</p>";
echo "<span>$jsonObject->time</span>";
echo "</div>";
}
}
class Article
{
private $jsonString;
public function __construct()
{
$this->jsonString = '{"title": "This is title", "text": "This is text", "author": "Pikabu Member"}';
}
public function publish()
{
$jsonObject = json_decode($this->jsonString);
echo "<div class='article'>";
echo "<h2>$jsonObject->title</h2>";
echo "<p>$jsonObject->text</p>";
echo "<span>$jsonObject->author</span>";
echo "</div>";
}
}
$contents[] = new News();
$contents[] = new Article();
foreach($contents as $content){
$content->publish();
echo PHP_EOL."<div class='divider'></div>".PHP_EOL;
}
?>
Классы News и Article имеют одно приватное свойство jsonString. Заполняют его при создании объекта в конструкторе и выводят информацию из этого свойства в методе publish.
Таким образом мы можем использовать один метод для отрисовки данных из разных классов. И в главном потоке программы использовать простую конструкцию foreach(){$content->publish} для вывода разных отформатированных блоков.
Кто-то также говорит, что многие реализации должны принадлежать одному интерфейсу. То есть, мы должны определить общий интерфейс для классов его реализующих. На этапе вызова общего метода проверить принадлежность к интерфейсу. Таким образом мы сможем расширять программу многими классами и не задумываться об изменении главной программы.
<?php
interface IContent
{
function publish();
}
class News implements IContent
{
private $jsonString;
public function __construct()
{
$this->jsonString = '{"title": "This is title", "text": "This is text", "time": "2024-01-13 12:00:00"}';
}
public function publish()
{
$jsonObject = json_decode($this->jsonString);
echo "<div class='news'>";
echo "<h1>$jsonObject->title</h1>";
echo "<p>$jsonObject->text</p>";
echo "<span>$jsonObject->time</span>";
echo "</div>";
}
}
class Article implements IContent
{
private $jsonString;
public function __construct()
{
$this->jsonString = '{"title": "This is title", "text": "This is text", "author": "Pikabu Member"}';
}
public function publish()
{
$jsonObject = json_decode($this->jsonString);
echo "<div class='article'>";
echo "<h2>$jsonObject->title</h2>";
echo "<p>$jsonObject->text</p>";
echo "<span>$jsonObject->author</span>";
echo "</div>";
}
}
$contents[] = new News();
$contents[] = new Article();
foreach($contents as $content){
if ($content instanceof IContent) {
$content->publish();
echo PHP_EOL."<div class='divider'></div>".PHP_EOL;
}
}
?>
Вместо заключения. Хоть праздники уже закончились, но статью я дописал только сейчас, поэтому - Желаю всем счастливого нового года, денег, тепла и мира.
Спасибо всем, кто смог дочитать этот пост до конца несмотря на ужасное оформление, примеры на php и общую безграмотность. Отдельное спасибо всем, кто заходит и подписывается на ютуб канал. Я начинаю готовить видео на тему ООП. Может быть примеры кода на видео будут более понятны.
Ссылки на полезные документы:
Ну и в конце добавлю опрос
Что рассмотрим в следующий раз?