Поведенческие паттерны проектирования в PHP

·

4 min read

Поведенческие паттерны помогают наладить эффективное взаимодействие между объектами в системе.


1. Strategy (Стратегия)

Позволяет выбрать алгоритм выполнения задачи во время выполнения программы.
📌 Когда использовать? Если нужно переключаться между разными способами выполнения задачи.

Пример

interface PaymentStrategy {
    public function pay(int $amount): string;
}

// Реализации стратегий
class PayPalPayment implements PaymentStrategy {
    public function pay(int $amount): string {
        return "Paid $amount via PayPal";
    }
}

class CreditCardPayment implements PaymentStrategy {
    public function pay(int $amount): string {
        return "Paid $amount via Credit Card";
    }
}

// Контекст, который использует стратегию
class PaymentContext {
    private PaymentStrategy $strategy;

    public function __construct(PaymentStrategy $strategy) {
        $this->strategy = $strategy;
    }

    public function pay(int $amount): string {
        return $this->strategy->pay($amount);
    }
}

// Использование
$context = new PaymentContext(new PayPalPayment());
echo $context->pay(100); // "Paid 100 via PayPal"

$context = new PaymentContext(new CreditCardPayment());
echo $context->pay(200); // "Paid 200 via Credit Card"

2. Observer (Наблюдатель)

Позволяет объекту уведомлять других объектов о своем изменении.
📌 Когда использовать? Когда нужно автоматически обновлять несколько объектов при изменении одного.

Пример

interface Observer {
    public function update(string $message): void;
}

class User implements Observer {
    private string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }

    public function update(string $message): void {
        echo "$this->name received notification: $message\n";
    }
}

class NewsChannel {
    private array $subscribers = [];

    public function subscribe(Observer $observer): void {
        $this->subscribers[] = $observer;
    }

    public function notify(string $message): void {
        foreach ($this->subscribers as $subscriber) {
            $subscriber->update($message);
        }
    }
}

// Использование
$news = new NewsChannel();
$user1 = new User("Alice");
$user2 = new User("Bob");

$news->subscribe($user1);
$news->subscribe($user2);

$news->notify("Breaking News!"); 
// "Alice received notification: Breaking News!"
// "Bob received notification: Breaking News!"

3. Command (Команда)

Инкапсулирует запрос в объект, позволяя передавать его как параметр.
📌 Когда использовать? Когда нужно передавать запросы в виде объектов.

Пример

interface Command {
    public function execute(): string;
}

class Light {
    public function turnOn(): string {
        return "Light is ON";
    }

    public function turnOff(): string {
        return "Light is OFF";
    }
}

class TurnOnLightCommand implements Command {
    private Light $light;

    public function __construct(Light $light) {
        $this->light = $light;
    }

    public function execute(): string {
        return $this->light->turnOn();
    }
}

class RemoteControl {
    private Command $command;

    public function setCommand(Command $command): void {
        $this->command = $command;
    }

    public function pressButton(): string {
        return $this->command->execute();
    }
}

// Использование
$light = new Light();
$turnOnCommand = new TurnOnLightCommand($light);

$remote = new RemoteControl();
$remote->setCommand($turnOnCommand);
echo $remote->pressButton(); // "Light is ON"

4. Chain of Responsibility (Цепочка обязанностей)

Передает запрос по цепочке обработчиков, пока кто-то не обработает его.
📌 Когда использовать? Когда нужно передавать запросы по цепочке обработчиков.

Пример

abstract class Handler {
    private ?Handler $next = null;

    public function setNext(Handler $handler): Handler {
        $this->next = $handler;
        return $handler;
    }

    public function handle(string $request): string {
        if ($this->next) {
            return $this->next->handle($request);
        }
        return "Request not handled";
    }
}

class AuthHandler extends Handler {
    public function handle(string $request): string {
        if ($request === "auth") {
            return "User authenticated";
        }
        return parent::handle($request);
    }
}

class LoggingHandler extends Handler {
    public function handle(string $request): string {
        if ($request === "log") {
            return "Logging request";
        }
        return parent::handle($request);
    }
}

// Использование
$auth = new AuthHandler();
$log = new LoggingHandler();

$auth->setNext($log);

echo $auth->handle("auth"); // "User authenticated"
echo $auth->handle("log");  // "Logging request"
echo $auth->handle("other"); // "Request not handled"

5. State (Состояние)

Позволяет объекту менять свое поведение в зависимости от состояния.
📌 Когда использовать? Когда объект должен изменять свое поведение в зависимости от внутреннего состояния.

Пример

interface State {
    public function handle(): string;
}

class HappyState implements State {
    public function handle(): string {
        return "I'm happy!";
    }
}

class SadState implements State {
    public function handle(): string {
        return "I'm sad...";
    }
}

class Person {
    private State $state;

    public function __construct(State $state) {
        $this->state = $state;
    }

    public function setState(State $state): void {
        $this->state = $state;
    }

    public function talk(): string {
        return $this->state->handle();
    }
}

// Использование
$person = new Person(new HappyState());
echo $person->talk(); // "I'm happy!"

$person->setState(new SadState());
echo $person->talk(); // "I'm sad..."

6. Mediator (Посредник)

Посредник управляет взаимодействием между объектами, уменьшая зависимость между ними.
📌 Когда использовать? Когда много объектов взаимодействуют между собой.

Пример

class ChatRoom {
    public function showMessage(string $user, string $message): void {
        echo "$user: $message\n";
    }
}

class User {
    private string $name;
    private ChatRoom $chat;

    public function __construct(string $name, ChatRoom $chat) {
        $this->name = $name;
        $this->chat = $chat;
    }

    public function send(string $message): void {
        $this->chat->showMessage($this->name, $message);
    }
}

// Использование
$chat = new ChatRoom();
$user1 = new User("Alice", $chat);
$user2 = new User("Bob", $chat);

$user1->send("Hello, Bob!"); // "Alice: Hello, Bob!"
$user2->send("Hi, Alice!");  // "Bob: Hi, Alice!"

Вывод

ПаттернКогда использовать?
StrategyЕсли нужно переключаться между алгоритмами
ObserverЕсли нужно уведомлять объекты об изменениях
CommandЕсли нужно передавать запросы как объекты
Chain of ResponsibilityЕсли запрос должен пройти через несколько обработчиков
StateЕсли объект должен менять поведение в зависимости от состояния
MediatorЕсли нужно управлять взаимодействием множества объектов