O padrão Bridge é um padrão de design estrutural que tem como objetivo principal desacoplar uma abstração da sua implementação, de modo que os dois possam variar independentemente. Este padrão é usado para resolver problemas relacionados à rigidez da arquitetura, especialmente em casos onde a abstração e a implementação podem ter variações independentes. Os problemas que o Bridge visa resolver incluem:

  1. Prevenção de Explosão de Classes: Em sistemas onde abstrações e implementações estão fortemente acopladas, qualquer mudança em uma delas pode exigir mudanças correspondentes na outra. Isso pode levar a uma proliferação de subclasses para lidar com todas as combinações possíveis, tornando o sistema difícil de entender e manter.

  2. Flexibilidade e Extensibilidade: O Bridge permite extensão tanto das abstrações quanto das implementações de forma independente. Isso torna o sistema mais flexível e facilita a adição de novas funcionalidades.

  3. Mudanças Independentes: Permite que mudanças sejam feitas na abstração ou na implementação sem afetar a outra parte. Isso é particularmente útil em ambientes onde a abstração e a implementação podem mudar por razões diferentes e em ritmos diferentes.

  4. Compartilhamento de Implementação: O Bridge também permite compartilhar uma implementação entre múltiplas abstrações, o que pode ser mais eficiente em termos de recursos.

Como Funciona

O padrão Bridge geralmente envolve quatro componentes principais:

  • Abstração: Define a interface abstrata e mantém uma referência a um objeto da “Implementação”.
  • Abstração Refinada: Estende ou refina a interface definida pela Abstração.
  • Implementador (Implementação): Define a interface para as classes de implementação. Não tem de corresponder exatamente à interface de abstração, mas deve ser capaz de ser implementada por ambas as abstrações e abstrações refinadas.
  • Implementador Concreto: Fornece uma implementação específica da interface do Implementador.

Exemplo

Um exemplo clássico do uso do Bridge é em sistemas de janelas de interface gráfica, onde uma abstração de janela pode ter várias implementações diferentes (como janelas para Windows, MacOS ou Linux). O padrão Bridge permite que o código da janela (a abstração) opere independentemente do sistema operacional subjacente (a implementação).

Exemplo de implementação

Vamos criar um sistema simples para gerenciamento de mensagens, onde diferentes tipos de mensagens (como texto curto ou mensagem detalhada) podem ser enviadas por diferentes meios de comunicação (como email ou SMS). Usaremos o padrão Bridge para separar a "abstração" (tipos de mensagem) da "implementação" (meios de envio).

Definindo a Interface do Implementador (Meio de Envio)

public interface EnviadorMensagem {
    void enviarMensagem(String mensagem);
}

Implementadores Concretos (Email e SMS)

public class EnviadorEmail implements EnviadorMensagem {
    @Override
    public void enviarMensagem(String mensagem) {
        System.out.println("Enviando mensagem por Email: " + mensagem);
    }
}
 
public class EnviadorSMS implements EnviadorMensagem {
    @Override
    public void enviarMensagem(String mensagem) {
        System.out.println("Enviando mensagem por SMS: " + mensagem);
    }
}

Abstração (Mensagem)

public abstract class Mensagem {
    protected EnviadorMensagem enviador;
 
    protected Mensagem(EnviadorMensagem enviador) {
        this.enviador = enviador;
    }
 
    public abstract void enviar(String conteudo);
}

o uso do protected é uma prática comum em classes abstratas que pretendem passar certos campos ou métodos para suas subclasses, mantendo um controle sobre o acesso e a modificação desses membros.

Abstrações Refinadas (Texto Curto e Mensagem Detalhada)

public class MensagemTextoCurto extends Mensagem {
    public MensagemTextoCurto(EnviadorMensagem enviador) {
        super(enviador);
    }
 
    @Override
    public void enviar(String conteudo) {
        // Processamento específico para texto curto
        enviador.enviarMensagem(conteudo);
    }
}
 
public class MensagemDetalhada extends Mensagem {
    public MensagemDetalhada(EnviadorMensagem enviador) {
        super(enviador);
    }
 
    @Override
    public void enviar(String conteudo) {
        // Processamento específico para mensagem detalhada
        String conteudoDetalhado = "Detalhe: " + conteudo;
        enviador.enviarMensagem(conteudoDetalhado);
    }
}

Utilizando o Padrão Bridge

public class Main {
    public static void main(String[] args) {
        Mensagem mensagem1 = new MensagemTextoCurto(new EnviadorEmail());
        mensagem1.enviar("Olá, mensagem rápida!");
 
        Mensagem mensagem2 = new MensagemDetalhada(new EnviadorSMS());
        mensagem2.enviar("Informações importantes...");
    }
}

Neste exemplo, MensagemTextoCurto e MensagemDetalhada são diferentes tipos de mensagens (abstrações) que usam EnviadorMensagem (implementador) para enviar mensagens. O padrão Bridge permite que as abstrações de mensagens utilizem diferentes meios de envio (email ou SMS) sem estar diretamente acopladas a eles. Isso facilita a extensão do sistema para suportar novos tipos de mensagens ou novos meios de envio, sem alterar as classes existentes.