Паттерны проектирования в PHP: продвинутый уровень
Паттерны проектирования (design patterns) — это проверенные решения типовых задач, возникающих при разработке программного обеспечения. На начальном уровне разработчики изучают Singleton, Factory или Adapter. Однако в сложных, высоконагруженных или гибких системах на PHP требуются более мощные и абстрактные шаблоны. В этой статье мы разберём четыре продвинутых паттерна с реальными примерами кода на PHP: Стратегия, Фабричный метод, Наблюдатель и Состояние.
1. Паттерн "Стратегия" (Strategy)
Назначение: Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Позволяет изменять поведение объекта во время выполнения без изменения его кода.
Пример: Система обработки платежей. У нас есть несколько способов оплаты: кредитная карта, PayPal, криптовалюта. Каждый способ имеет свою логику валидации и обработки.
<?php
// Интерфейс стратегии
interface PaymentStrategy
{
public function pay(float $amount): void;
public function validate(): bool;
}
// Конкретные стратегии
class CreditCardPayment implements PaymentStrategy
{
private string $cardNumber;
private string $cvv;
public function __construct(string $cardNumber, string $cvv)
{
$this->cardNumber = $cardNumber;
$this->cvv = $cvv;
}
public function validate(): bool
{
// Простая проверка длины номера карты
return strlen($this->cardNumber) === 16 && strlen($this->cvv) === 3;
}
public function pay(float $amount): void
{
if (!$this->validate()) {
throw new \InvalidArgumentException("Неверные данные карты");
}
echo "Оплачено {$amount} через кредитную карту {$this->cardNumber}.<br>";
}
}
class PayPalPayment implements PaymentStrategy
{
private string $email;
public function __construct(string $email)
{
$this->email = $email;
}
public function validate(): bool
{
return filter_var($this->email, FILTER_VALIDATE_EMAIL) !== false;
}
public function pay(float $amount): void
{
if (!$this->validate()) {
throw new \InvalidArgumentException("Неверный email PayPal");
}
echo "Оплачено {$amount} через PayPal ({$this->email}).<br>";
}
}
// Контекст (клиент)
class OrderProcessor
{
private PaymentStrategy $strategy;
public function setPaymentStrategy(PaymentStrategy $strategy): void
{
$this->strategy = $strategy;
}
public function processOrder(float $total): void
{
if (!isset($this->strategy)) {
throw new \RuntimeException("Способ оплаты не выбран");
}
$this->strategy->pay($total);
}
}
// Использование
$order = new OrderProcessor();
$order->setPaymentStrategy(new CreditCardPayment('1234567812345678', '123'));
$order->processOrder(150.00);
// Вывод: Оплачено 150 через кредитную карту 1234567812345678.
?>
Плюсы: Легко добавлять новые алгоритмы (новые способы оплаты) без изменения существующего кода. Код становится гибким и тестируемым.
2. Паттерн "Фабричный метод" (Factory Method)
Назначение: Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. Это базовый паттерн для всех порождающих шаблонов.
Пример: Генерация разных типов отчётов (PDF, HTML, JSON) в зависимости от запроса пользователя.
<?php
// Абстрактный продукт
interface Report
{
public function generate(): string;
}
// Конкретные продукты
class PdfReport implements Report
{
public function generate(): string
{
return "Генерация PDF-отчёта с использованием TCPDF...";
}
}
class HtmlReport implements Report
{
public function generate(): string
{
return "Генерация HTML-отчёта с использованием Twig...";
}
}
// Абстрактный создатель (Creator)
abstract class ReportFactory
{
abstract protected function createReport(): Report;
public function generateReport(): string
{
$report = $this->createReport();
// Дополнительная логика (логирование, проверка прав)
return $report->generate();
}
}
// Конкретные создатели
class PdfReportFactory extends ReportFactory
{
protected function createReport(): Report
{
return new PdfReport();
}
}
class HtmlReportFactory extends ReportFactory
{
protected function createReport(): Report
{
return new HtmlReport();
}
}
// Использование
$factory = new PdfReportFactory();
echo $factory->generateReport(); // Генерация PDF-отчёта...
$factory = new HtmlReportFactory();
echo $factory->generateReport(); // Генерация HTML-отчёта...
?>
Плюсы: Инкапсуляция логики создания объектов. Подходит для случаев, когда тип объекта заранее неизвестен или должен определяться на этапе выполнения.
3. Паттерн "Наблюдатель" (Observer)
Назначение: Создаёт механизм подписки, позволяющий одним объектам (наблюдателям) следить за изменениями состояния других объектов (субъектов).
Пример: Система уведомлений: когда заказ меняет статус (например, "отправлен"), нужно оповестить клиента по email и администратора по SMS.
<?php
// Интерфейс наблюдателя
interface Observer
{
public function update(string $event, $data): void;
}
// Интерфейс субъекта
interface Observable
{
public function attach(Observer $observer): void;
public function detach(Observer $observer): void;
public function notify(string $event, $data): void;
}
// Конкретный субъект (заказ)
class Order implements Observable
{
private array $observers = [];
private string $status;
public function attach(Observer $observer): void
{
$this->observers[] = $observer;
}
public function detach(Observer $observer): void
{
$this->observers = array_filter($this->observers, fn($o) => $o !== $observer);
}
public function notify(string $event, $data): void
{
foreach ($this->observers as $observer) {
$observer->update($event, $data);
}
}
public function setStatus(string $newStatus): void
{
$oldStatus = $this->status ?? 'нов