Паттерны проектирования в PHP на продвинутом уровне: примеры кода

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Паттерны проектирования в 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 ?? 'нов                

Блоги

Рекомендаций по книгам