Menu
Coddy logo textTech

Полиморфизм через интерфейсы

Часть раздела Объектно-ориентированное программирование путешествия по 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 использоваться взаимозаменяемо на основе общего поведения, делая ваш код более гибким и не зависящим от конкретных реализаций.

challenge icon

Задание

Легко

Создадим систему хранения, демонстрирующую полиморфизм на основе интерфейсов. Ты создашь разные внутренние системы хранения — файловую систему и базу данных, — у которых нет ничего общего, кроме того, что обе реализуют один и тот же интерфейс хранения. Одна функция будет работать с любым типом хранилища, показывая, как интерфейсы обеспечивают полиморфизм между совершенно несвязанными классами.

Ты организуешь код в четырёх файлах:

  • Storable.php: Define интерфейс Storable с двумя сигнатурами method: save($key, $data) и retrieve($key). Этот контракт гарантирует, что любая система хранения может сохранять и извлекать data независимо от того, как она реализована внутри.
  • FileStorage.php: Create class FileStorage, который implements Storable. Подключи файл интерфейса. У class должно быть private property $directory, задаваемое через constructor. Implement save($key, $data), чтобы возвращать "Saving '[data]' to file [directory]/[key].txt". Implement retrieve($key), чтобы возвращать "Reading from file [directory]/[key].txt".
  • DatabaseStorage.php: Create class DatabaseStorage, который также implements Storable. Подключи файл интерфейса. У class должно быть private property $tableName, задаваемое через constructor. Implement save($key, $data), чтобы возвращать "Inserting '[data]' into table [tableName] with key [key]". Implement retrieve($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
?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор PHP