Pular para o conteúdo principal

Design Patterns Clássicos (GoF): Relevância e Aplicações Modernas

Publicado em 22 de dezembro de 202520 min de leitura
Imagem de tecnologia relacionada ao artigo design-patterns-classicos-relevancia-moderna

Design Patterns Clássicos (GoF): Relevância e Aplicações Modernas

Referência Clássica: O livro "Gang of Four" (1994) define 23 padrões. Este artigo avalia sua relevância em arquiteturas modernas.

Se você fez faculdade de Ciência da Computação, provavelmente teve um livro de capa dura cinza, pesado e intimidante, empurrado para cima de você: "Design Patterns: Elements of Reusable Object-Oriented Software". Publicado em 1994 por Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides (apelidados carinhosamente de Gang of Four ou GoF), este livro foi a bíblia do desenvolvimento de software por quase 30 anos.

Mas o mundo mudou drasticamente.

Em 1994, C++ e Smalltalk eram reis. A memória RAM era medida em megabytes. "Nuvem" era apenas algo que chovia. Hoje, vivemos na era do JavaScript, Rust, Go, Microsserviços, Serverless e IA Generativa.

A pergunta que muitos desenvolvedores seniores discutem em fóruns e comunidades é: esses padrões ainda importam? Ou será que aplicar um Abstract Factory em um código React moderno é apenas excesso de engenharia e nostalgia?

Mais do que decorar nomes de padrões, entender o raciocínio por trás deles é o que separa o codificador do arquiteto. Vamos revisitar o legado da Gang of Four sob uma lente pragmática, separando o que ainda é essencial do que se tornou apenas ruído técnico em um mundo dominado por microsserviços e programação funcional.

O Que Envelheceu Mal (Os Anti-Patterns de Hoje)

Alguns padrões que eram considerados "boas práticas" nos anos 90 são hoje amplamente vistos como armadilhas ou "code smells".

1. Singleton: O Rei dos Anti-Patterns

O Singleton foi criado com uma intenção nobre: garantir que uma classe tenha apenas uma instância (como uma conexão de banco de dados). Na prática, porém, o Singleton se tornou uma variável global glorificada.

  • O Problema: Ele introduz estado global oculto. Se o Teste A muda o Singleton, o Teste B falha misteriosamente. Ele torna o código impossível de testar em paralelo e cria um acoplamento invisível entre módulos distantes.
  • A Solução Moderna: Injeção de Dependência (DI). Você cria a instância uma vez na inicialização da aplicação e a passa explicitamente para quem precisa. É transparente, testável e honesto.

2. Visitor, Iterator e Command (A Morte por Verbosidade)

Muitos padrões comportamentais da GoF existiam apenas porque Java e C++ (na época) não tinham funções de primeira classe (First-Class Functions).

  • O Command Pattern: Criar uma classe inteira (implements Command) com um método execute() só para passar uma ação para um botão?
  • A Solução Moderna: Apenas passe uma função! Em JavaScript, Rust ou Python, você passa uma lambda/closure. O padrão "Command" inteiro vira uma linha de código: button.onClick(() => save()).
  • Foi a Programação Funcional que matou a necessidade dessa burocracia orientada a objetos.

O Que é Eterno (Ainda Usamos Todo Dia)

Design Patterns Clássicos (GoF): Relevância e Aplicações Modernas

Apesar das críticas, a maioria dos padrões da GoF descreve problemas fundamentais de arquitetura que não desaparecem só porque mudamos de linguagem.

1. Adapter (O Tradutor Universal)

Este é imortal. Você tem uma biblioteca antiga que espera dados em XML, mas sua nova API retorna JSON. Você não vai reescrever a biblioteca nem a API. Você escreve um Adapter. No React, usamos isso o tempo todo quando criamos componentes "Wrapper" para normalizar props de bibliotecas de terceiros.

2. Strategy (O Camaleão)

O Strategy permite que você troque o algoritmo de uma classe em tempo de execução.

  • Exemplo Real: Um sistema de e-commerce que calcula frete. Você tem estratégias diferentes: CorreiosStrategy, FedexStrategy, RetiradaStrategy. O código do carrinho de compras não precisa saber como o frete é calculado, apenas que existe uma estratégia injetada que retorna um valor.
  • Em cenários modernos, isso é crucial para Feature Flags. "Se o usuário é Beta, use o algoritmo de Recomendação V2; senão, use o V1".

Padrões na Prática: Exemplos Reais

Para entender como esses conceitos se traduzem em código moderno, vamos ver implementações práticas usando JavaScript/TypeScript, que fogem da verbosidade clássica do Java dos anos 90.

1. Strategy Pattern (Injeção de Comportamento)

Em vez de grandes blocos de if/else para diferentes meios de pagamento, usamos o Strategy.

javascript
// Definindo as estratégias como funções simples
const strategies = {
  CREDIT_CARD: (amount) => amount * 1.05, // 5% de taxa
  PIX: (amount) => amount * 0.90,        // 10% de desconto
  BOLETO: (amount) => amount              // Sem alteração
};

// O contexto que utiliza a estratégia
function calculateTotal(amount, type) {
  const strategy = strategies[type] || strategies.BOLETO;
  return strategy(amount);
}

console.log(calculateTotal(100, 'PIX')); // 90

2. Adapter Pattern (Compatibilidade)

Útil para integrar uma nova API que tem nomes de campos diferentes da sua aplicação legada.

javascript
// API Nova (Externa)
const newApiUser = {
  user_full_name: "Joel Fernandes",
  contact_email: "joel@exemplo.com"
};

// Nosso Adapter
class UserAdapter {
  constructor(apiUser) {
    this.name = apiUser.user_full_name;
    this.email = apiUser.contact_email;
  }
}

// A aplicação agora usa o objeto normalizado
const user = new UserAdapter(newApiUser);
console.log(user.name); // "Joel Fernandes"

3. Facade (A Fachada Amigável)

Sistemas modernos são complexos. Um Facade fornece uma interface simples para um subsistema complexo.

  • Exemplo Real: Quando você chama sdk.iniciarPagamento(), por trás dessa linha simples (a Fachada), o SDK pode estar autenticando, criptografando dados, fazendo 3 chamadas HTTP e retry automático. Para quem usa, é simples. Para quem faz, é o caos organizado por trás de um Facade.

A Influência da Nuvem e Microsserviços

Muitos padrões de design "subiram de nível". Eles saíram de dentro do código (classes) para a arquitetura (serviços).

  • Observer Pattern: No livro, era sobre objetos observando mudanças em outros objetos. Na nuvem, isso virou Arquitetura Orientada a Eventos (Event-Driven). O Kafka ou o RabbitMQ são a implementação em escala planetária do padrão Observer. Um serviço publica um evento ("PedidoCriado") e outros 50 serviços reagem a ele, sem que o publicador saiba quem eles são.
  • Proxy Pattern: Virou o API Gateway ou o Service Mesh (Istio/Linkerd). Ele intercepta chamadas de rede para adicionar segurança, log ou cache, exatamente como o padrão Proxy fazia com chamadas de método.

Conclusão: Use, Mas Não Abuse

Estudar Design Patterns ainda é vital, não para copiar e colar código, mas para ter um vocabulário comum. Quando um desenvolvedor diz "vamos usar um Factory aqui", todos na sala entendem a intenção arquitetural sem precisar ver o diagrama.

O perigo está no "Patternitis" — a doença do desenvolvedor júnior que acabou de ler o livro e quer enfiar um Decorator onde uma simples função resolveria.

"Padrões são soluções recorrentes para problemas recorrentes. Se você não tem o problema, não use a solução."


Glossário Técnico

  • Design Pattern: Solução reutilizável para um problema comum em design de software.
  • GoF (Gang of Four): Os 4 autores do livro clássico de Design Patterns (1994).
  • Singleton: Padrão que garante uma única instância de uma classe (controverso hoje).
  • Injeção de Dependência (DI): Técnica onde as dependências são passadas para um objeto em vez de serem criadas por ele.

Referências

  1. Erich Gamma et al. Design Patterns: Elements of Reusable Object-Oriented Software. O clássico original.
  2. Robert C. Martin. Clean Architecture. Arquitetura moderna.
  3. Refactoring.guru. Catalog of Design Patterns. Catálogo visual excelente.
Imagem de tecnologia relacionada ao artigo design-patterns-classicos-relevancia-moderna