Что такое полиморфизм в ООП?

Ответ

Полиморфизм (букв. "много форм") — это принцип ООП, который позволяет объектам с одинаковым интерфейсом (типом) вести себя по-разному в зависимости от их конкретной реализации. Код, работающий с базовым типом, автоматически может работать и с любым его производным типом.

В PHP полиморфизм реализуется преимущественно через:

1. Переопределение методов при наследовании

Дочерний класс предоставляет свою конкретную реализацию метода, уже объявленного в родительском классе.

abstract class NotificationSender {
    abstract public function send(string $message): void;
}

class EmailSender extends NotificationSender {
    public function send(string $message): void {
        echo "Отправляем email с текстом: {$message}n";
    }
}

class SmsSender extends NotificationSender {
    public function send(string $message): void {
        echo "Отправляем SMS: {$message}n";
    }
}

// Полиморфный вызов
function notifyUser(NotificationSender $sender, string $alert) {
    $sender->send($alert); // Неважно, какой именно sender пришёл. Он знает, КАК отправлять.
}

$email = new EmailSender();
$sms = new SmsSender();

notifyUser($email, "Ваш заказ готов!"); // Отправляем email...
notifyUser($sms, "Код подтверждения: 1234"); // Отправляем SMS...

2. Использование интерфейсов

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

interface LoggerInterface {
    public function log(string $level, string $message): void;
}

class FileLogger implements LoggerInterface {
    public function log(string $level, string $message): void {
        file_put_contents('app.log', "[$level] $messagen", FILE_APPEND);
    }
}

class DatabaseLogger implements LoggerInterface {
    public function log(string $level, string $message): void {
        // Вставка записи $level, $message в таблицу логов
        echo "[DB LOG] [$level] $messagen";
    }
}

class Application {
    private LoggerInterface $logger;

    public function __construct(LoggerInterface $logger) {
        $this->logger = $logger; // Принимаем ЛЮБОЙ логгер, реализующий интерфейс
    }

    public function run() {
        $this->logger->log('INFO', 'Приложение запущено');
        // ... бизнес-логика
        $this->logger->log('ERROR', 'Что-то пошло не так');
    }
}

// Запуск с разными логгерами
$appWithFileLog = new Application(new FileLogger());
$appWithDbLog = new Application(new DatabaseLogger());

$appWithFileLog->run(); // Пишет в файл
$appWithDbLog->run();   // Пишет в базу данных

Суть полиморфизма: программа оперирует абстракциями (NotificationSender, LoggerInterface), а конкретное поведение определяется во время выполнения (runtime) типом реального объекта (EmailSender, DatabaseLogger). Это делает систему гибкой, расширяемой и легко тестируемой (зависимости можно подменять).

Ответ 18+ 🔞

Да ты посмотри, какая хитрая жопа этот полиморфизм! В переводе — «много форм», но по сути это просто умение одной и той же хуйни работать по-разному, в зависимости от того, какую конкретно подсунули реализацию. Если код написан под общий тип, он спокойно сожрёт любой дочерний класс, и даже не поперхнётся.

В PHP это делается в основном двумя путями, и оба — овердохуища удобные.

1. Когда наследники переписывают родительские методы

Представь, есть у тебя абстрактный класс-родитель, который говорит: «Вот метод send(), но как его делать — я, блядь, не знаю». А его дети уже конкретно решают, как именно отправлять сообщение: по email или SMS.

abstract class NotificationSender {
    abstract public function send(string $message): void;
}

class EmailSender extends NotificationSender {
    public function send(string $message): void {
        echo "Отправляем email с текстом: {$message}n";
    }
}

class SmsSender extends NotificationSender {
    public function send(string $message): void {
        echo "Отправляем SMS: {$message}n";
    }
}

// А вот тут и начинается магия, ёпта
function notifyUser(NotificationSender $sender, string $alert) {
    $sender->send($alert); // Похуй, что за sender пришёл. Он сам знает, что делать.
}

$email = new EmailSender();
$sms = new SmsSender();

notifyUser($email, "Ваш заказ готов!"); // Отправляем email...
notifyUser($sms, "Код подтверждения: 1234"); // Отправляем SMS...

Видишь? Функция notifyUser работает с абстракцией NotificationSender, а на деле туда можно запихнуть что угодно, лишь бы у этого «чего угодно» был метод send(). И оно само разберётся.

2. Через интерфейсы — чистейшая форма полиморфизма

Тут вообще доверия ебать ноль, но зато строго. Интерфейс — это контракт. Подписался на него — будь добр, реализуй все указанные методы. А дальше хоть в файл пиши, хоть в базу, хоть на Луну сигнал отправляй.

interface LoggerInterface {
    public function log(string $level, string $message): void;
}

class FileLogger implements LoggerInterface {
    public function log(string $level, string $message): void {
        file_put_contents('app.log', "[$level] $messagen", FILE_APPEND);
    }
}

class DatabaseLogger implements LoggerInterface {
    public function log(string $level, string $message): void {
        // Вставка записи $level, $message в таблицу логов
        echo "[DB LOG] [$level] $messagen";
    }
}

class Application {
    private LoggerInterface $logger;

    public function __construct(LoggerInterface $logger) {
        $this->logger = $logger; // Принимаем ЛЮБОЙ логгер, который подписал контракт
    }

    public function run() {
        $this->logger->log('INFO', 'Приложение запущено');
        // ... бизнес-логика
        $this->logger->log('ERROR', 'Что-то пошло не так');
    }
}

// Запускаем приложение с разными логгерами
$appWithFileLog = new Application(new FileLogger());
$appWithDbLog = new Application(new DatabaseLogger());

$appWithFileLog->run(); // Пишет в файл
$appWithDbLog->run();   // Пишет в базу данных

Соль в чём: весь твой код завязан на абстракции (LoggerInterface), а не на конкретных классах. Это как если бы ты говорил: «Мне нужен кто-то, кто умеет логировать». А уж придёт ли тебе файловый логгер, или логгер в базу, или вообще логгер, который кричит в окно — тебе похуй. Главное, чтобы метод log() был. Система становится гибкой, её легко расширять и тестировать, потому что зависимости можно подменять, как перчатки. Вот и вся магия, ебать копать.

Видео-ответы