Neste artigo
O Padrão de Projeto Bridge separa uma abstração da sua implementação, para que as duas possam mudar de forma independente. Em vez de uma única hierarquia de classes que cresce em duas direções, você cria duas hierarquias menores e liga uma à outra por composição.
O nome vem dessa ligação: o objeto de um lado guarda uma referência para o objeto do outro, e essa referência é a ponte (bridge, em inglês).

Observe no diagrama os participantes do padrão:
- Abstraction (Notification): a classe que o cliente usa. Guarda a referência para a implementação.
- RefinedAbstraction (OrderShippedNotification e PaymentFailedNotification): as variações da abstração.
- Implementor (Channel): a interface do lado da implementação.
- ConcreteImplementor (EmailChannel e SmsChannel): as implementações concretas.
Qual problema o Bridge resolve?
Pense em um sistema que avisa o cliente sobre o pedido. Existem tipos de aviso (pedido enviado, pagamento recusado) e existem canais de envio (e-mail, SMS). São duas coisas que mudam por motivos diferentes: o tipo de aviso muda quando o negócio cria uma regra nova, e o canal muda quando a empresa contrata um serviço novo.
A solução com herança cria uma classe para cada combinação:
<?php
class OrderShippedEmail {}
class OrderShippedSms {}
class PaymentFailedEmail {}
class PaymentFailedSms {}
// Um canal novo (push) exige mais duas classes:
class OrderShippedPush {}
class PaymentFailedPush {}
Com dois tipos e dois canais são quatro classes, e o código de envio por e-mail fica repetido em duas delas. Cada tipo ou canal novo aumenta o problema:
| Tipos de aviso | Canais | Classes com herança | Classes com Bridge |
|---|---|---|---|
| 2 | 2 | 4 | 4 |
| 3 | 3 | 9 | 6 |
| 4 | 5 | 20 | 9 |
Com o Bridge, os tipos de aviso ficam em uma hierarquia, os canais em outra, e o aviso recebe o canal pelo construtor.
Exemplo do padrão Bridge em PHP
Vamos para o código, começando pelo lado da implementação. A interface Channel diz o que todo canal sabe fazer:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Bridge;
interface Channel
{
public function send(string $recipient, string $text): void;
}
Cada canal concreto cuida só de como enviar. Para o exemplo ficar testável, eles apenas imprimem o que seria enviado:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Bridge;
final class EmailChannel implements Channel
{
public function send(string $recipient, string $text): void
{
printf("Email to %s: %s\n", $recipient, $text);
}
}
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Bridge;
final class SmsChannel implements Channel
{
public function send(string $recipient, string $text): void
{
printf("SMS to %s: %s\n", $recipient, $text);
}
}
Agora o lado da abstração. A classe Notification guarda o canal recebido no construtor. Essa propriedade é a ponte entre as duas hierarquias:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Bridge;
abstract class Notification
{
protected Channel $channel;
public function __construct(Channel $channel)
{
$this->channel = $channel;
}
abstract public function notify(string $recipient): void;
}
Cada tipo de aviso estende Notification e cuida só do que dizer. O envio fica com o canal:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Bridge;
final class OrderShippedNotification extends Notification
{
private string $orderId;
public function __construct(Channel $channel, string $orderId)
{
parent::__construct($channel);
$this->orderId = $orderId;
}
public function notify(string $recipient): void
{
$this->channel->send($recipient, sprintf('Order %s has been shipped', $this->orderId));
}
}
O aviso de pagamento recusado segue o mesmo caminho:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Structural\Bridge;
final class PaymentFailedNotification extends Notification
{
private string $orderId;
public function __construct(Channel $channel, string $orderId)
{
parent::__construct($channel);
$this->orderId = $orderId;
}
public function notify(string $recipient): void
{
$this->channel->send($recipient, sprintf('Payment for order %s failed', $this->orderId));
}
}
Repare que nenhuma notificação sabe se vai sair por e-mail ou por SMS, e nenhum canal sabe qual aviso está enviando.
Testando o exemplo
Os testes combinam os dois tipos de aviso com os dois canais. O último cria um canal novo (push) dentro do próprio teste, sem tocar em nenhuma classe de notificação:
<?php
declare(strict_types=1);
namespace Growthdev\DesignPatterns\Tests\Structural\Bridge;
use Growthdev\DesignPatterns\Structural\Bridge\Channel;
use Growthdev\DesignPatterns\Structural\Bridge\EmailChannel;
use Growthdev\DesignPatterns\Structural\Bridge\OrderShippedNotification;
use Growthdev\DesignPatterns\Structural\Bridge\PaymentFailedNotification;
use Growthdev\DesignPatterns\Structural\Bridge\SmsChannel;
use PHPUnit\Framework\TestCase;
final class BridgeTest extends TestCase
{
public function testExpectOrderShippedToBeSentByEmail(): void
{
$notification = new OrderShippedNotification(new EmailChannel(), '1001');
$notification->notify('dev@example.com');
$this->expectOutputString("Email to dev@example.com: Order 1001 has been shipped\n");
}
public function testExpectSameNotificationToBeSentBySms(): void
{
$notification = new OrderShippedNotification(new SmsChannel(), '1001');
$notification->notify('+55 11 90000-0000');
$this->expectOutputString("SMS to +55 11 90000-0000: Order 1001 has been shipped\n");
}
public function testExpectPaymentFailedToWorkWithEveryChannel(): void
{
(new PaymentFailedNotification(new EmailChannel(), '1002'))->notify('dev@example.com');
(new PaymentFailedNotification(new SmsChannel(), '1002'))->notify('+55 11 90000-0000');
$this->expectOutputString(
"Email to dev@example.com: Payment for order 1002 failed\n"
. "SMS to +55 11 90000-0000: Payment for order 1002 failed\n"
);
}
public function testExpectNewChannelWithoutChangingNotifications(): void
{
$push = new class implements Channel {
/** @var string[] */
public array $sent = [];
public function send(string $recipient, string $text): void
{
$this->sent[] = sprintf('Push to %s: %s', $recipient, $text);
}
};
(new OrderShippedNotification($push, '1001'))->notify('device-42');
(new PaymentFailedNotification($push, '1002'))->notify('device-42');
$this->assertSame(
[
'Push to device-42: Order 1001 has been shipped',
'Push to device-42: Payment for order 1002 failed',
],
$push->sent
);
}
}
É esse último teste que mostra o ganho: um canal novo é uma classe nova, e todos os avisos que já existem funcionam com ele.
Quando usar o padrão Bridge?
- Quando uma classe cresce em duas direções independentes (tipo e canal, relatório e formato, forma e cor) e a herança começa a pedir uma classe por combinação.
- Quando você quer escolher a implementação em tempo de execução, por exemplo o canal preferido de cada cliente.
- Quando os dois lados precisam evoluir separados, mantidos por equipes ou pacotes diferentes.
Cuidados ao usar o Bridge
- Se só um dos lados varia, o Bridge é estrutura demais: uma interface com as suas implementações já resolve.
- A interface do lado da implementação precisa servir para todos os canais. Se um canal pedir um dado que os outros não usam, como o assunto do e-mail, a interface começa a ganhar parâmetros que só um deles aproveita.
- O padrão acrescenta um nível de indireção: para saber o que um aviso faz, é preciso ler a notificação e o canal.
Bridge e Adapter: qual a diferença?
Os dois padrões usam composição para ligar um objeto a outro, e por isso o código deles se parece. A diferença está no momento e no motivo. Segundo o livro GoF, o Adapter costuma ser aplicado a sistemas que já foram projetados, enquanto o Bridge é pensado no início do projeto.
| Padrão | Quando entra | Objetivo |
|---|---|---|
| Bridge | No desenho, antes de as classes existirem | Deixar abstração e implementação mudarem separadas |
| Adapter | Depois, com as classes já prontas | Fazer duas interfaces incompatíveis trabalharem juntas |
Esse é mais um padrão estrutural do livro GoF. Para conhecer os outros, veja o artigo Resumo dos Padrões de Projetos.
O código completo deste exemplo, com os testes, está no meu GitHub, na pasta src/Structural/Bridge do repositório growthdev-repo/design-patterns.
Veja também: o resumo dos 23 padrões de projeto GoF e os outros padrões estruturais já publicados: Adapter, Composite, Decorator, Facade, Flyweight, Proxy.
Confiança Sempre!!!
Fontes:
- [1] GAMMA, Erich et al. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos.
- [2] FREEMAN, Eric; ROBSON, Elisabeth; BATES, Bert; SIERRA, Kathy. Head First Design Patterns.

Continue a conversa