Паттерн Мост в PHP
Короткий скринкаст: паттерн «Мост» (Bridge) — разделяем абстракцию и реализацию, чтобы они менялись независимо.
Описание
Мы делаем пульт и устройство как две независимые оси. Интерфейс Device описывает ТВ, а абстрактный RemoteControl в конструкторе получает устройство. Конкретный BasicRemote не знает, какое устройство внутри, — он лишь вызывает контракт. Добавьте радио или умный пульт — ни одна сторона не пострадает.
UML-диаграмма
Код
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 (уведомление и каналы). Абстракция и способ доставки развиваются независимо, а реализация подставляется из конфигурации.