Padrões estruturais: Composite e Bridge
1. Introdução aos Padrões Estruturais Composite e Bridge
Padrões estruturais na Arquitetura de Software tratam da composição de classes e objetos para formar estruturas maiores e mais complexas. Eles respondem à pergunta fundamental: "Como organizamos nossas entidades de software para que o sistema seja flexível, extensível e de fácil manutenção?"
Composite e Bridge são dois padrões estruturais que resolvem problemas distintos, mas igualmente críticos. O Composite lida com a representação de hierarquias parte-todo, permitindo que objetos individuais e composições de objetos sejam tratados uniformemente. O Bridge, por sua vez, aborda o desacoplamento entre uma abstração e sua implementação, permitindo que ambas variem independentemente.
Quando a complexidade das hierarquias de classes começa a gerar explosão combinatória ou quando estruturas recursivas precisam ser tratadas de forma transparente, esses padrões tornam-se ferramentas essenciais no arsenal do arquiteto de software.
2. Padrão Composite: Composição Hierárquica de Objetos
O Composite estrutura objetos em árvores para representar hierarquias parte-todo. Sua estrutura é composta por três elementos:
- Component: interface comum que declara operações para objetos da composição
- Leaf: objeto folha que implementa o comportamento primitivo
- Composite: objeto que armazena componentes filhos e implementa operações delegando-as aos filhos
O mecanismo de recursão é o coração do Composite. Operações como render() ou calcularTamanho() são chamadas no Composite, que as delega recursivamente para todos os filhos.
// Interface Component
interface ComponenteGrafico {
void renderizar()
void adicionar(ComponenteGrafico c)
void remover(ComponenteGrafico c)
}
// Leaf
class Botao implements ComponenteGrafico {
void renderizar() { /* renderiza botão */ }
void adicionar(ComponenteGrafico c) { /* não aplicável */ }
void remover(ComponenteGrafico c) { /* não aplicável */ }
}
// Composite
class Painel implements ComponenteGrafico {
List<ComponenteGrafico> filhos
void renderizar() {
for (filho in filhos) filho.renderizar()
}
void adicionar(ComponenteGrafico c) { filhos.add(c) }
void remover(ComponenteGrafico c) { filhos.remove(c) }
}
// Uso
ComponenteGrafico janela = new Painel()
ComponenteGrafico painel = new Painel()
painel.adicionar(new Botao())
janela.adicionar(painel)
janela.renderizar() // Renderiza recursivamente
Em um sistema de interface gráfica, uma Janela (Composite) contém Painéis (Composite) que contêm Botões (Leaf). A operação renderizar() percorre toda a árvore, garantindo que cada elemento seja desenhado corretamente.
3. Aplicações Avançadas do Composite em Arquitetura
O padrão Composite encontra aplicações naturais em diversos domínios arquiteturais:
Gerenciamento de estruturas de documentos: Editores de texto e processadores XML representam documentos como árvores de elementos. Cada nó pode ser um parágrafo, seção ou tabela (Composite), enquanto caracteres e imagens são folhas. Operações como exportarParaHTML() percorrem recursivamente a árvore.
Sistemas de arquivos e diretórios: Arquivos (Leaf) e Diretórios (Composite) compartilham operações como calcularTamanho() e buscarArquivos(). Um diretório delega essas operações para seus filhos, somando tamanhos ou agregando resultados de busca.
Trade-offs importantes:
- Complexidade de navegação em árvores profundas pode impactar desempenho
- Transparência vs. segurança: operações como adicionar() em Leafs geram exceções em tempo de execução
4. Padrão Bridge: Separando Abstração da Implementação
O Bridge desacopla uma abstração de sua implementação, permitindo que ambas evoluam independentemente. Sua estrutura é composta por:
- Abstraction: define a interface de alto nível
- RefinedAbstraction: estende a abstração base
- Implementor: define a interface para implementações concretas
- ConcreteImplementor: implementa o Implementor
O princípio de desacoplamento evita a explosão combinatória de classes. Sem Bridge, teríamos N × M classes para N abstrações e M implementações. Com Bridge, temos N + M classes.
// Implementor
interface APIDesenhista {
void desenharCirculo(float x, float y, float raio)
void desenharQuadrado(float x, float y, float lado)
}
// ConcreteImplementors
class OpenGLDesenhista implements APIDesenhista {
void desenharCirculo(float x, float y, float raio) { /* OpenGL */ }
void desenharQuadrado(float x, float y, float lado) { /* OpenGL */ }
}
class DirectXDesenhista implements APIDesenhista {
void desenharCirculo(float x, float y, float raio) { /* DirectX */ }
void desenharQuadrado(float x, float y, float lado) { /* DirectX */ }
}
// Abstraction
abstract class Forma {
protected APIDesenhista api
Forma(APIDesenhista api) { this.api = api }
abstract void desenhar()
}
// RefinedAbstraction
class Circulo extends Forma {
float x, y, raio
void desenhar() { api.desenharCirculo(x, y, raio) }
}
// Uso
Forma circulo = new Circulo(5, 5, 10, new OpenGLDesenhista())
circulo.desenhar() // Desenha com OpenGL
5. Bridge na Prática: Cenários de Arquitetura de Software
Adaptação multiplataforma: Abstrações de interface do usuário (janelas, menus, diálogos) com implementações específicas para Windows, Linux e macOS. O código da abstração permanece idêntico, enquanto as implementações lidam com peculiaridades de cada plataforma.
Persistência e drivers de banco de dados: A abstração Repositorio define operações como salvar(), buscar(), remover(). Implementações concretas usam SQL, NoSQL ou arquivos. A variação independente permite que novas estratégias de persistência sejam adicionadas sem modificar a lógica de negócio.
Variação independente: Bridge permite evoluir abstrações e implementações sem impacto mútuo. Podemos adicionar novas formas geométricas sem modificar APIs de desenho, ou novas APIs sem modificar as formas existentes.
6. Comparação entre Composite e Bridge
Semelhanças: Ambos promovem baixo acoplamento e flexibilidade estrutural, utilizando composição sobre herança.
Diferenças fundamentais:
- Composite foca em hierarquias parte-todo e tratamento uniforme de objetos individuais e compostos
- Bridge foca em separar duas dimensões de variação: abstração e implementação
Critérios de escolha:
- Use Composite quando o domínio possui estruturas recursivas naturais (árvores, documentos, sistemas de arquivos)
- Use Bridge quando duas dimensões de variação precisam ser independentes (formas × APIs, interfaces × plataformas)
7. Integração com Padrões Vizinhos da Série
Composite + Decorator: O Decorator pode ser aplicado em árvores Composite para adicionar responsabilidades dinamicamente. Por exemplo, adicionar bordas ou sombras a componentes gráficos sem modificar suas classes.
Bridge + Adapter: O Adapter pode adaptar implementações concretas existentes para a interface Implementor do Bridge, permitindo reutilizar código legado.
Bridge + Strategy: Bridge fornece a estrutura estável para que o Strategy varie comportamentos em tempo de execução. A abstração Bridge delega para implementações que podem usar diferentes estratégias algorítmicas.
8. Conclusão e Boas Práticas
Composite e Bridge são padrões complementares que abordam problemas estruturais distintos. Composite oferece composição transparente para hierarquias parte-todo, enquanto Bridge proporciona desacoplamento bidimensional entre abstração e implementação.
Armadilhas comuns:
- Superengenharia em cenários simples: nem toda hierarquia precisa de Composite, nem toda variação precisa de Bridge
- Confusão entre Composite e hierarquias de herança: Composite usa composição, não herança, para estruturar objetos
- Bridge excessivo: aplicar Bridge onde uma única dimensão varia é overengineering
Recomendações finais: Aplique Composite em domínios com árvores naturais (documentos, sistemas de arquivos, interfaces gráficas). Aplique Bridge quando duas dimensões variam independentemente e a explosão combinatória de classes se torna um problema real.
Referências
- Composite Pattern - Refactoring Guru — Explicação detalhada do padrão Composite com exemplos em múltiplas linguagens e diagramas UML
- Bridge Pattern - Refactoring Guru — Guia completo do padrão Bridge com exemplos práticos e casos de uso
- Composite Pattern - SourceMaking — Tutorial aprofundado sobre Composite, incluindo estrutura, participantes e aplicações
- Bridge Pattern - SourceMaking — Recurso detalhado sobre Bridge com exemplos de implementação e discussão de trade-offs
- Design Patterns: Elements of Reusable Object-Oriented Software (GoF) — O livro clássico que introduziu os padrões Composite e Bridge, com definições formais e exemplos originais