Полиморфизм через интерфейсы
Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 34 из 91.
В предыдущем уроке вы увидели полиморфизм через наследование, при котором дочерние классы переопределяют методы родительских классов. Интерфейсы предоставляют ещё один мощный способ实现ить полиморфизм, особенно при работе с несвязанными классами, которые имеют общее поведение.
С интерфейсами полиморфизм исходит из самого контракта. Любой class, реализующий интерфейс, гарантирует наличие определённых method, независимо от его иерархии наследования:
<?php
interface Notifiable {
public function send(string $message);
}
class EmailNotifier implements Notifiable {
public function send(string $message) {
return "Email: " . $message;
}
}
class SMSNotifier implements Notifiable {
public function send(string $message) {
return "SMS: " . $message;
}
}
class PushNotifier implements Notifiable {
public function send(string $message) {
return "Push: " . $message;
}
}
function notify(Notifiable $notifier, string $message) {
return $notifier->send($message);
}
echo notify(new EmailNotifier(), "Hello") . "\n";
echo notify(new SMSNotifier(), "Hello") . "\n";
echo notify(new PushNotifier(), "Hello");
Результат:
Email: Hello
SMS: Hello
Push: HelloФункция notify() работает с любым объектом Notifiable. У этих классов нет общего родительского класса — они полностью не связаны друг с другом, за исключением того, что реализуют один и тот же интерфейс. Это ключевое преимущество перед полиморфизмом на основе наследования: вы можете объединять несвязанные классы по их возможностям, а не по их происхождению.
Ключевой момент: Полиморфизм на основе interface позволяет несвязанным class использоваться взаимозаменяемо на основе общего поведения, делая ваш код более гибким и не зависящим от конкретных реализаций.
Задание
ЛегкоСоздадим систему хранения, демонстрирующую полиморфизм на основе интерфейсов. Ты создашь разные внутренние системы хранения — файловую систему и базу данных, — у которых нет ничего общего, кроме того, что обе реализуют один и тот же интерфейс хранения. Одна функция будет работать с любым типом хранилища, показывая, как интерфейсы обеспечивают полиморфизм между совершенно несвязанными классами.
Ты организуешь код в четырёх файлах:
Storable.php: Define интерфейсStorableс двумя сигнатурами method:save($key, $data)иretrieve($key). Этот контракт гарантирует, что любая система хранения может сохранять и извлекать data независимо от того, как она реализована внутри.FileStorage.php: Create classFileStorage, который implementsStorable. Подключи файл интерфейса. У class должно быть private property$directory, задаваемое через constructor. Implementsave($key, $data), чтобы возвращать"Saving '[data]' to file [directory]/[key].txt". Implementretrieve($key), чтобы возвращать"Reading from file [directory]/[key].txt".DatabaseStorage.php: Create classDatabaseStorage, который также implementsStorable. Подключи файл интерфейса. У class должно быть private property$tableName, задаваемое через constructor. Implementsave($key, $data), чтобы возвращать"Inserting '[data]' into table [tableName] with key [key]". Implementretrieve($key), чтобы возвращать"Selecting from table [tableName] where key = [key]".main.php: Подключи оба файла хранения. Create function с именемstoreData, которая accepts параметр типаStorable, key и data. function должна возвращать result вызоваsave()с key и data. Create ещё одну function с именемfetchData, которая accepts параметр типаStorableи key, возвращая result вызоваretrieve().
Ты получишь четыре входных значения: путь к directory, имя таблицы, key и некоторые data для сохранения. Create FileStorage с directory и DatabaseStorage с именем таблицы. Используй свою function storeData(), чтобы сохранить data сначала с помощью файлового хранилища, затем с помощью хранилища базы данных. Выведи каждый result в отдельной строке. Затем используй fetchData(), чтобы извлечь data из файлового хранилища и вывести этот result.
Обрати внимание, как storeData() и fetchData() работают одинаково с обоими типами хранилищ. Их интересует только то, что объект implements Storable. Файловая система и база данных имеют совершенно разные внутренние реализации, но благодаря контракту интерфейса они взаимозаменяемы.
Попробуйте сами
<?php
require_once 'FileStorage.php';
require_once 'DatabaseStorage.php';
// Прочитать входные данные
$directory = trim(fgets(STDIN));
$tableName = trim(fgets(STDIN));
$key = trim(fgets(STDIN));
$data = trim(fgets(STDIN));
// TODO: Создать функцию storeData, которая принимает Storable, key и data
// Она должна возвращать результат вызова save() с key и data
// TODO: Создать функцию fetchData, которая принимает Storable и key
// Она должна возвращать результат вызова retrieve() с key
// TODO: Создать экземпляр FileStorage с directory
// TODO: Создать экземпляр DatabaseStorage с table name
// TODO: Использовать storeData() для сохранения данных с file storage, затем вывести результат
// TODO: Использовать storeData() для сохранения данных с database storage, затем вывести результат
// TODO: Use fetchData() to retrieve from file storage, then print the result
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Повторение — простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыПовторение — иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateМодификаторы доступа подробноГеттеры и сеттерыСокрытие информацииПродвижение конструктора (8.0)Повторение — система учёта студентов2Пространства имён и автозагрузка
Введение в пространства имёнКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerПовторение — структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс и абстрактный классАннотация типов с интерфейсамиПовторение — калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeПовторение — пользовательская коллекция3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищённые свойстваСвойства только для чтения (PHP 8.1)Повторение — менеджер банковского счёта6Полиморфизм
Повторение переопределения методовПолиморфизм через интерфейсыАннотации типов и объединённые типыПозднее статическое связываниеПовторение — обработчик платежей9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования, часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн StrategyПотренируйтесь самостоятельно: Онлайн-компилятор PHP