Bridge
Visão Geral
Bridge separa uma abstração da sua implementação, de modo que as duas possam variar independentemente.
É o padrão que resolve a explosão combinatória de hierarquias — o problema que aparece quando duas dimensões de variação são modeladas por herança.
Problema
Você tem duas dimensões que variam. Modeladas por herança, o número de classes é o produto delas.
2 formas × 3 renderizadores = 6 classes
CirculoVetorial CirculoRaster CirculoASCII
QuadradoVetorial QuadradoRaster QuadradoASCII
Adicionar uma forma exige três classes. Adicionar um renderizador exige duas. Com quatro formas e quatro renderizadores, dezesseis.
Bridge substitui o produto pela soma:
2 formas + 3 renderizadores = 5 classes
A forma tem uma referência ao renderizador. As duas hierarquias existem separadamente e se combinam por composição.
Conceitos Centrais
A estrutura
A abstração delega ao implementador. Note que implementador não é a implementação da abstração — é uma segunda hierarquia, com sua própria interface, em outro nível de granularidade.
Bridge não é Adapter
Distinção que causa confusão constante.
Adapter é aplicado depois, para compatibilizar coisas que já existem e não foram projetadas para trabalhar juntas.
Bridge é projetado antes, para que duas hierarquias possam evoluir separadas.
A diferença é de intenção e de momento: Adapter conserta; Bridge previne.
Bridge não é Strategy
Também confundido, e a distinção é mais sutil.
Strategy troca um algoritmo. A interface da estratégia representa uma decisão isolada, geralmente com um método.
Bridge separa duas dimensões estruturais. O implementador costuma ter várias operações primitivas que a abstração combina.
Estruturalmente parecidos; a diferença está em o que varia — algoritmo versus dimensão de implementação — e em quantas operações a interface tem.
Quando Usar
- Duas dimensões de variação que crescem independentemente.
- É preciso trocar a implementação em tempo de execução.
- A implementação precisa ser invisível ao cliente.
- Você está prestes a criar a terceira ou quarta classe de uma hierarquia que multiplica.
Quando Não Usar
Quando há uma dimensão só de variação. Herança simples ou Strategy resolvem, e Bridge adiciona uma hierarquia sem motivo.
Quando uma das dimensões tem uma implementação só. O produto ainda não explodiu; ver YAGNI.
Preventivamente. É um dos padrões mais caros de aplicar cedo, porque exige projetar a interface do implementador — as operações primitivas certas — sem conhecer as variações reais. Adivinhar errado ali produz uma interface que cada implementador precisa contornar.
Quando as duas dimensões não são independentes. Se certas combinações não fazem sentido, a separação é artificial e o código acaba com verificações de compatibilidade.
Alternativas
- Strategy — quando o que varia é um algoritmo.
- Composição simples — passar a dependência sem hierarquia formal de abstração.
- Herança — enquanto houver uma dimensão só.
- Funções de primeira classe — quando o implementador tem uma operação.
Trade-offs
| Bridge | Herança em duas dimensões |
|---|---|
| Classes somam | Classes multiplicam |
| Implementação trocável em execução | Fixa em compilação |
| Duas hierarquias a projetar | Uma |
| Interface do implementador precisa estar certa | Sem interface intermediária |
| Indireção adicional | Direto |
Modos de Falha
Interface do implementador mal escolhida. As primitivas não servem a todos os implementadores; alguns precisam de operações que não existem, outros deixam métodos vazios.
Dimensões não independentes. Combinações inválidas exigem verificação em execução.
Bridge com um implementador. Hierarquia sem variação.
Vazamento da implementação. A abstração expõe detalhes do implementador, e o cliente passa a depender de qual está em uso.
Erros Comuns
Confundir com Adapter. Momento e intenção diferentes.
Confundir com Strategy. Uma dimensão versus duas.
Aplicar antes da explosão. Espere a terceira ou quarta classe do produto.
Projetar a interface do implementador a partir de um caso. Ela precisa servir a todos.
Onde ele aparece na prática
Drivers de banco. A hierarquia de Connection, Statement e ResultSet é a
abstração; cada driver é uma implementação. As duas variam: novos tipos de
operação de um lado, novos bancos do outro.
Bibliotecas gráficas multiplataforma. A abstração de janela e desenho é uma hierarquia; o sistema gráfico nativo de cada plataforma é outra.
Abstrações de log. A API que o código usa é a abstração; os appenders que escrevem em arquivo, console ou rede são os implementadores.
O denominador comum: em todos, quem projetou a interface do implementador tinha várias implementações reais em mãos. É a condição que o padrão exige e que raramente existe quando alguém propõe aplicá-lo cedo.
Exemplo Real
Um sistema de notificações modelava canal e formato por herança, e chegou a doze
classes: EmailHtml, EmailTexto, SmsTexto, PushJson, PushTexto, e assim
por diante. Metade das combinações não fazia sentido e existia como classe que
lançava exceção.
A separação em Bridge foi feita depois que o problema apareceu, e a interface do implementador foi extraída a partir das seis combinações que de fato funcionavam.
Resultado: quatro canais e três formatos, com uma tabela explícita de quais combinações são válidas — porque elas de fato não são todas independentes.
Esse último ponto é o mais honesto do caso: Bridge pressupõe independência entre as dimensões, e aqui a independência era parcial. A solução ficou sendo Bridge com uma verificação de compatibilidade, que é menos elegante que o padrão puro e é o que o domínio exigia.
Como reconhecer que você precisa dele
O sinal mais confiável está nos nomes das classes: dois adjetivos que vêm de listas diferentes.
RelatorioMensalPDF, RelatorioAnualPDF, RelatorioMensalExcel — "mensal" e
"anual" vêm de uma lista, "PDF" e "Excel" de outra. O produto das duas é o número
de classes.
Três verificações que confirmam:
Conte as listas. Se os nomes das classes podem ser gerados combinando dois ou mais conjuntos de palavras, há mais de uma dimensão.
Procure classes que não existem. Se RelatorioAnualExcel deveria existir e
não existe, ou existe lançando exceção, a hierarquia já não comporta o produto.
Veja o que uma dimensão nova custaria. Adicionar um formato exige quantas classes? Se for mais de uma, o custo é multiplicativo.
Uma ressalva importante: encontrar o padrão não significa que Bridge é a resposta. Se uma das dimensões tem duas variantes estáveis há anos, o produto é pequeno e gerenciável. O padrão se paga quando ambas as dimensões crescem — e crescer é uma afirmação sobre o histórico, não sobre a intuição.
Conceitos Relacionados
- Adapter — compatibilizar o que já existe.
- Strategy — variar um algoritmo.
- Abstract Factory — frequentemente usado para criar o par abstração-implementador coerente.
Exercício Prático
Procure no seu sistema hierarquias cujo número de classes é o produto de duas listas — dois adjetivos no nome da classe costuma denunciar.
Para cada uma, verifique se todas as combinações são válidas. Se não forem, Bridge puro não se aplica sem tratamento adicional.
Perguntas de Entrevista
- Qual a diferença entre Bridge e Adapter?
- E entre Bridge e Strategy?
- Por que aplicar Bridge preventivamente é arriscado?
Para Aprofundar
- Gamma, Erich et al. Design Patterns. Addison-Wesley, 1994.