Наследование, интерфейсы и полиморфизм
Создание гибкой архитектуры приложения с помощью интерфейсов и абстрактных классов на примере интеграции платежных шлюзов (Stripe, Vipps).
В рамках Project-Based Learning мы подошли к реальной задаче: интернет-магазин должен принимать оплату через разные системы — Stripe и Vipps (популярна в Норвегии). Если мы напишем логику оплаты жестко под Stripe, добавить Vipps будет огромной болью. Здесь на помощь приходят Интерфейсы и Полиморфизм. Интерфейс — это строгий контракт. Он не содержит реализации (тела функций), он только говорит: "Любой класс, который реализует этот интерфейс, ОБЯЗАН иметь такие-то методы". Полиморфизм (многообразие форм) позволяет нашему Checkout-контроллеру работать с любым платежным шлюзом одинаково, даже не зная, Stripe это, Vipps или PayPal. Контроллер просто вызывает метод pay(), потому что интерфейс гарантирует его наличие.
<?php
// Создаем контракт (интерфейс)
interface PaymentGateway {
public function pay(float $amount): bool;
}
// Класс для Stripe реализует интерфейс
class StripeGateway implements PaymentGateway {
public function pay(float $amount): bool {
// Здесь сложная логика обращения к API Stripe
echo "Оплачено $" . $amount . " через Stripe.\n";
return true;
}
}
// Класс для Vipps также реализует интерфейс
class VippsGateway implements PaymentGateway {
public function pay(float $amount): bool {
// Здесь логика обращения к API Vipps
echo "Оплачено $" . $amount . " через Vipps.\n";
return true;
}
}
?>
Как это применяется на практике? В нашем классе OrderProcessor мы не будем указывать конкретный класс Stripe или Vipps. Мы укажем интерфейс PaymentGateway. Этот паттерн называется Внедрение зависимостей (Dependency Injection). Благодаря этому мы можем легко переключать платежные системы. Если клиент из США — мы передаем в процессор объект StripeGateway. Если из Норвегии — VippsGateway. Код самого обработчика заказов при этом не меняется ни на одну строчку! Это и есть сила ООП и правильной архитектуры. Типичная ошибка новичков — писать огромные конструкции if ($paymentType == 'stripe') { ... } else if (...) { ... } прямо в контроллере. Использование интерфейсов делает код чистым, масштабируемым и легко тестируемым.
<?php
// Класс обработки заказа
class OrderProcessor {
// Внедрение зависимости через интерфейс
public function __construct(private PaymentGateway $gateway) {}
public function checkout(float $totalAmount) {
// Полиморфизм в действии: мы не знаем, какой это шлюз, но метод pay() точно есть
if ($this->gateway->pay($totalAmount)) {
echo "Заказ успешно оформлен!";
} else {
echo "Ошибка оплаты.";
}
}
}
// Использование
$stripe = new StripeGateway();
$processor = new OrderProcessor($stripe);
$processor->checkout(150.00);
?>