Padrões estruturais: Adapter e Facade
1. Introdução aos Padrões Estruturais
Os padrões estruturais são um dos pilares da arquitetura de software, responsáveis por organizar classes e objetos em estruturas maiores, garantindo flexibilidade e reutilização. Diferentemente dos padrões criacionais, que tratam da instanciação de objetos, e dos comportamentais, que focam na comunicação entre entidades, os padrões estruturais lidam com a composição de interfaces e a forma como os componentes se relacionam.
Dentro desse grupo, dois padrões se destacam por resolverem problemas complementares: o Adapter, que permite que interfaces incompatíveis trabalhem juntas, e o Facade, que simplifica o acesso a subsistemas complexos. Ambos são essenciais para arquitetos que buscam sistemas modulares e de fácil manutenção.
2. Padrão Adapter: Conceito e Fundamentos
O Adapter resolve um dos problemas mais comuns em sistemas de software: a incompatibilidade entre interfaces. Imagine que você precisa integrar um sistema legado de pagamentos com uma API moderna de e-commerce. O sistema antigo espera chamadas no formato XML, enquanto a nova API exige JSON. Sem um adaptador, seria necessário reescrever todo o código legado.
A analogia clássica é o adaptador de tomada elétrica: um dispositivo que converte um plugue de formato diferente para que ele se encaixe em uma tomada específica. No software, o padrão atua da mesma forma, convertendo a interface de uma classe (Adaptee) em outra interface esperada pelo cliente (Target).
A estrutura básica envolve quatro elementos:
- Target: a interface que o cliente espera.
- Adaptee: a classe existente com interface incompatível.
- Adapter: a classe que converte a interface do Adaptee para o Target.
- Client: o código que utiliza o Target.
3. Implementação do Adapter: Abordagens e Exemplos
Existem duas abordagens principais para implementar o Adapter: por herança (Adapter de classe) e por composição (Adapter de objeto). A primeira utiliza herança múltipla (em linguagens que suportam) e é mais restrita; a segunda, mais flexível, usa composição e é preferida na maioria dos cenários.
Considere um sistema legado de pagamentos que processa transações via SOAP:
// Interface esperada pelo novo sistema (Target)
public interface PaymentProcessor {
void processPayment(double amount);
}
// Sistema legado (Adaptee)
public class LegacyPaymentSystem {
public void makeTransaction(String xmlData) {
// Lógica legada em SOAP
}
}
// Adapter usando composição
public class PaymentAdapter implements PaymentProcessor {
private LegacyPaymentSystem legacySystem;
public PaymentAdapter(LegacyPaymentSystem legacy) {
this.legacySystem = legacy;
}
@Override
public void processPayment(double amount) {
String xml = convertToXML(amount);
legacySystem.makeTransaction(xml);
}
private String convertToXML(double amount) {
return "<payment><amount>" + amount + "</amount></payment>";
}
}
// Cliente
public class ECommerceApp {
public static void main(String[] args) {
PaymentProcessor processor = new PaymentAdapter(new LegacyPaymentSystem());
processor.processPayment(150.0);
}
}
4. Variações e Aplicações Avançadas do Adapter
O Adapter pode ser bidirecional, quando ambos os lados precisam de adaptação simultânea. Por exemplo, em uma migração gradual de sistemas, onde o novo sistema precisa conversar com o antigo e vice-versa.
Em arquiteturas de microserviços, o Adapter é frequentemente usado para adaptar protocolos de comunicação. Um serviço que espera gRPC pode receber chamadas REST através de um adaptador que traduz as requisições. Isso é comum em cenários de integração com sistemas legados que só expõem SOAP.
Importante diferenciar o Adapter do Proxy (já abordado na série): enquanto o Proxy controla o acesso a um objeto (adiando criação, adicionando segurança), o Adapter foca exclusivamente na conversão de interfaces. Eles podem ser combinados, mas têm propósitos distintos.
5. Padrão Facade: Conceito e Fundamentos
O Facade resolve o problema da complexidade excessiva de subsistemas interdependentes. Em sistemas grandes, como um e-commerce, o cliente pode precisar interagir com módulos de estoque, pagamento, envio e notificação. Sem uma fachada, o código cliente se torna acoplado a todas essas dependências.
A analogia é a fachada de um prédio: ela esconde a infraestrutura interna (canos, fiação, elevadores) e oferece uma interface simples para o visitante. No software, o Facade faz o mesmo, fornecendo um ponto único de entrada para funcionalidades complexas.
A estrutura é composta por:
- Facade: classe que oferece métodos simplificados.
- Subsistemas: classes complexas que realizam o trabalho real.
- Client: código que usa a Facade.
6. Implementação do Facade: Casos de Uso e Exemplos
Imagine um sistema de e-commerce com os subsistemas Estoque, Pagamento e Envio. Sem uma fachada, o cliente precisaria chamar cada um separadamente:
// Subsistemas complexos
public class InventoryService {
public boolean checkStock(String productId) { /* ... */ return true; }
public void reserveItem(String productId) { /* ... */ }
}
public class PaymentService {
public boolean processPayment(String userId, double amount) { /* ... */ return true; }
}
public class ShippingService {
public String scheduleDelivery(String address, String productId) { /* ... */ return "TRACK123"; }
}
// Facade simplificada
public class OrderFacade {
private InventoryService inventory;
private PaymentService payment;
private ShippingService shipping;
public OrderFacade() {
this.inventory = new InventoryService();
this.payment = new PaymentService();
this.shipping = new ShippingService();
}
public String placeOrder(String userId, String productId, String address, double amount) {
if (!inventory.checkStock(productId)) {
throw new RuntimeException("Estoque insuficiente");
}
inventory.reserveItem(productId);
if (!payment.processPayment(userId, amount)) {
throw new RuntimeException("Pagamento recusado");
}
return shipping.scheduleDelivery(address, productId);
}
}
// Cliente simplificado
public class Client {
public static void main(String[] args) {
OrderFacade facade = new OrderFacade();
String tracking = facade.placeOrder("user123", "PROD456", "Rua A, 100", 250.0);
System.out.println("Pedido realizado. Código: " + tracking);
}
}
Os benefícios são claros: redução de acoplamento, ponto único de entrada para manutenção e maior testabilidade (a fachada pode ser mockada).
7. Comparação e Relacionamento entre Adapter e Facade
Embora ambos sejam padrões estruturais, suas finalidades são distintas:
- Adapter: converte uma interface em outra para compatibilidade. Foco em integração.
- Facade: simplifica o acesso a um conjunto de interfaces. Foco em redução de complexidade.
O Adapter é usado quando você já tem um sistema funcionando, mas precisa que ele se encaixe em um novo contexto. O Facade é usado quando você quer esconder a complexidade de um subsistema do cliente.
Eles podem ser combinados: uma Facade pode usar Adapters internamente para integrar sistemas legados. Por exemplo, a fachada de um sistema de BI pode usar adaptadores para conectar-se a bancos de dados relacionais e NoSQL, oferecendo uma interface unificada de consulta.
8. Boas Práticas e Considerações Arquiteturais
Ao aplicar esses padrões, algumas práticas são importantes:
- Coesão: a fachada não deve virar um "deus objeto" que faz tudo. Mantenha-a focada em um domínio específico.
- Acoplamento: adaptadores devem ser leves e focados apenas na conversão. Evite lógica de negócio dentro deles.
- Testabilidade: ambos os padrões facilitam testes, pois isolam dependências. Use injeção de dependência para maior flexibilidade.
Armadilhas comuns incluem fachadas inchadas (que tentam cobrir todo o sistema) e adaptadores excessivos (que criam camadas desnecessárias). Avalie sempre se o padrão realmente simplifica o design.
Em relação aos próximos temas da série (Composite e Bridge), o Adapter e o Facade estabelecem a base para entender composição de objetos e abstração de interfaces, conceitos que serão aprofundados.
Referências
- Adapter Pattern - Refactoring Guru — Explicação detalhada com diagramas e exemplos em várias linguagens.
- Facade Pattern - Refactoring Guru — Guia completo sobre o padrão Facade com casos de uso reais.
- Adapter vs Facade - Stack Overflow Discussion — Discussão técnica sobre as diferenças entre os dois padrões.
- Structural Patterns in GoF - O'Reilly — Capítulo clássico do livro "Design Patterns" sobre padrões estruturais.
- Adapter Pattern in Microservices - DZone — Artigo sobre aplicação do Adapter em arquiteturas de microserviços.
- Facade Pattern: When and How to Use - Baeldung — Tutorial prático com implementação em Java do padrão Facade.