GoF, p. 151 também chamado de Handle/Body
Ponte
Bridge
Separa abstração de implementação para que as duas variem sem multiplicar classes.
Desacoplar uma abstração da sua implementação, de modo que as duas possam variar independentemente.
Controle remoto e aparelho
Existem vários tipos de controle remoto (básico, com voz, app no celular) e vários aparelhos (TV, rádio, ar-condicionado). Se cada combinação virasse uma classe, seriam 3 × 3 = 9 classes — e 4 × 4 = 16 quando você adicionasse mais um de cada. A ponte é o protocolo: todo controle fala “ligar / desligar / volume”, e cada aparelho implementa isso do seu jeito. Agora são 3 + 3 classes, e crescem somando, não multiplicando.
Duas dimensões de variação independentes na mesma hierarquia geram explosão combinatória. Forma × Cor: CírculoVermelho, CírculoAzul, QuadradoVermelho, QuadradoAzul… Cada nova cor multiplica o número de classes.
Extraia uma das dimensões para uma hierarquia separada (a Implementação) e faça a hierarquia original (a Abstração) referenciá-la por composição. A “ponte” é essa referência.
- Nomes de classe com duas dimensões coladas:
RelatorioPDFMensal,RelatorioExcelMensal… - Um
if (plataforma)dentro de cada método de uma hierarquia.
Estrutura
- Abstraction
- Interface de alto nível; mantém uma referência a Implementor e delega o trabalho real.
- RefinedAbstraction
- Variantes da lógica de alto nível.
- Implementor
- Interface das operações primitivas de baixo nível.
- ConcreteImplementor
- Implementações específicas de plataforma/tecnologia.
Implementação
// ── Implementação: COMO a mensagem sai (o canal) ──────
class Canal { enviar(destino, texto) { throw new Error('abstrato'); } }
class CanalEmail extends Canal { enviar(d, t) { return `✉️ e-mail → ${d}: ${t}`; } }
class CanalSMS extends Canal { enviar(d, t) { return `📱 sms → ${d}: ${t.slice(0, 140)}`; } }
class CanalSlack extends Canal { enviar(d, t) { return `💬 slack → #${d}: ${t}`; } }
// ── Abstração: O QUE é notificado (a política) ────────
class Notificacao {
constructor(canal) { this.canal = canal; } // ← 🌉 A PONTE
enviar(destino, texto) { return this.canal.enviar(destino, texto); }
}
class NotificacaoUrgente extends Notificacao {
enviar(destino, texto) {
// Refina o comportamento SEM saber qual canal está do outro lado.
return this.canal.enviar(destino, `🚨 URGENTE: ${texto.toUpperCase()}`);
}
}
class NotificacaoAgrupada extends Notificacao {
constructor(canal) { super(canal); this.fila = []; }
enviar(destino, texto) { this.fila.push(texto); return null; }
descarregar(destino) {
const resumo = `${this.fila.length} avisos:\n• ` + this.fila.join('\n• ');
this.fila = [];
return this.canal.enviar(destino, resumo);
}
}
// 3 canais × 3 políticas = 9 comportamentos com apenas 6 classes.
console.log(new NotificacaoUrgente(new CanalSMS()).enviar('+5511999', 'servidor caiu'));
console.log(new NotificacaoUrgente(new CanalSlack()).enviar('ops', 'servidor caiu'));
const diario = new NotificacaoAgrupada(new CanalEmail());
diario.enviar('ana@x.com', 'backup ok');
diario.enviar('ana@x.com', 'deploy ok');
console.log(diario.descarregar('ana@x.com')); - JDBC: a API é a abstração, os drivers são as implementações
- Device drivers de sistema operacional
- React Native / Flutter: mesma API de widget, renderizadores por plataforma
Consequências
- Cria classes e apps independentes de plataforma.
- O cliente enxerga só a abstração de alto nível.
- Abstração e implementação evoluem separadamente (Aberto/Fechado).
- Troca a implementação em tempo de execução.
- Complica o código quando a classe é coesa e não tem duas dimensões de variação reais.
- Você percebe uma explosão combinatória de subclasses (A × B).
- Você quer estender uma classe em dimensões ortogonais e independentes.
- Você precisa trocar a implementação em tempo de execução.
- Só existe uma implementação e nenhuma previsão de outra.
Relações com outros padrões
- Abstract Factory
- A fábrica pode montar a combinação abstração + implementação correta.
Verificação
Bridge e Strategy têm praticamente o mesmo diagrama. O que realmente as separa?
- A intenção: Bridge separa duas dimensões estruturais que variam; Strategy intercambia algoritmos para uma mesma tarefa
- Bridge usa herança e Strategy usa composição
- Strategy só funciona com métodos estáticos
- Bridge exige no mínimo três implementações concretas
Este é um exemplo clássico de que padrões se distinguem por INTENÇÃO, não por diagrama. A estrutura é a mesma; o problema que resolvem é diferente.