PHP Bridge Design Pattern Структурный паттерн

Паттерн Мост в PHP

Короткий скринкаст: паттерн «Мост» (Bridge) — разделяем абстракцию и реализацию, чтобы они менялись независимо.

Bridge Pattern — открыть страницу урока

Описание

Мы делаем пульт и устройство как две независимые оси. Интерфейс Device описывает ТВ, а абстрактный RemoteControl в конструкторе получает устройство. Конкретный BasicRemote не знает, какое устройство внутри, — он лишь вызывает контракт. Добавьте радио или умный пульт — ни одна сторона не пострадает.

UML-диаграмма

classDiagram class Device { <<interface>> +on() void +off() void +isOn() bool } class TV { -bool power +on() void +off() void +isOn() bool } class RemoteControl { <<abstract>> -device Device +togglePower() string } class BasicRemote { +togglePower() string } TV ..|> Device : implements RemoteControl *-- Device : holds BasicRemote --|> RemoteControl : extends

Код

Device.php

interface Device
{
    public function on(): void;
    public function off(): void;
    public function isOn(): bool;
}

TV.php

class TV implements Device
{
    private bool $power = false;

    public function on(): void
    {
        $this->power = true;
    }

    public function off(): void
    {
        $this->power = false;
    }

    public function isOn(): bool
    {
        return $this->power;
    }
}

RemoteControl.php

abstract class RemoteControl
{
    public function __construct(
        protected Device $device
    ) {}

    abstract public function togglePower(): string;
}

BasicRemote.php

class BasicRemote extends RemoteControl
{
    public function togglePower(): string
    {
        $this->device->isOn() ? $this->device->off() : $this->device->on();

        return $this->device->isOn() ? 'TV is ON' : 'TV is OFF';
    }
}

demo.php

require_once 'Device.php';
require_once 'TV.php';
require_once 'RemoteControl.php';
require_once 'BasicRemote.php';

$tv = new TV();
$remote = new BasicRemote($tv);

echo $remote->togglePower();
echo PHP_EOL;
echo $remote->togglePower();

Заметка: что развязывает мост

Мост идёт от анти-паттерна: без него классы пультов и устройств перемножаются (BasicTvRemote, AdvancedTvRemote, BasicRadioRemote…). С мостом две оси развиваются отдельно: пульт обращается к интерфейсу Device, а конкретные устройства подставляются в конструктор. Новый тип либо на стороне пульта, либо на стороне устройства — никогда вместе.

Пример результата

После php demo.php в терминале появится:

TV is ON
TV is OFF

Преимущества

  • Разделяет абстракцию и реализацию, позволяя им меняться независимо.
  • Убирает взрыв классов при комбинации двух измерений.
  • Реализацию можно выбирать и менять во время выполнения.
  • Упрощает добавление новых абстракций и новых реализаций.

Когда использовать

  • Когда у системы есть два независимо меняющихся измерения.
  • Когда нужно избежать постоянной привязки абстракции к реализации.
  • Когда реализацию нужно выбирать во время выполнения.
  • Когда комбинация «абстракция × реализация» иначе породит множество классов.

Использование в Laravel

В Laravel Мост — это почтовый слой: абстракция Illuminate\Mail\Mailer отделена от реализаций Illuminate\Mail\Transport\... (SMTP, log, array). Аналогично устроены Illuminate\Log\LogManager (логгер и его обработчики) и Illuminate\Notifications\ChannelManager (уведомление и каналы). Абстракция и способ доставки развиваются независимо, а реализация подставляется из конфигурации.