Design Patterns: o que são e como surgiram

1. O problema que deu origem aos Design Patterns

Entre os anos 1970 e 1980, a indústria de software enfrentou o que ficou conhecido como "crise do software". Projetos cada vez mais complexos resultavam em código monolítico, difícil de manter, testar e reutilizar. Sistemas eram construídos do zero, sem aproveitamento de soluções anteriores, e a comunicação entre equipes era prejudicada pela falta de uma linguagem comum para descrever problemas recorrentes.

Arquitetos e desenvolvedores começaram a perceber que certos problemas apareciam repetidamente, e que existiam soluções consolidadas — ainda que não formalizadas — para lidar com eles. Surgiu então a necessidade de catalogar essas "boas práticas" de forma estruturada.

A grande inspiração veio de fora da computação. Em 1977, o arquiteto Christopher Alexander publicou A Pattern Language, onde descrevia 253 padrões para projetar cidades, edifícios e espaços. A ideia central era que cada padrão resolvia um problema específico em um contexto, e podia ser reutilizado. Essa metáfora foi transportada para o software e deu origem ao conceito de Design Patterns.

2. Definição formal de um Design Pattern

Um Design Pattern é uma solução geral e reutilizável para um problema comum que ocorre em um contexto específico dentro do projeto de software. Não é um código pronto para copiar e colar, mas sim um modelo conceitual que pode ser adaptado.

Os quatro elementos essenciais de um padrão, conforme definido pelo GoF (Gang of Four), são:

  1. Nome: identifica o padrão e facilita a comunicação.
  2. Problema: descreve quando aplicar o padrão.
  3. Solução: os elementos que compõem a solução, suas responsabilidades e relacionamentos.
  4. Consequências: os resultados e trade-offs de aplicar o padrão.

É importante diferenciar padrões de algoritmos e frameworks. Um algoritmo é uma solução específica para um problema computacional; um padrão é uma solução arquitetural. Um framework já fornece implementação parcial; um padrão é um guia abstrato.

3. A obra seminal: "Gang of Four" (GoF) — 1994

Em 1994, Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides publicaram Design Patterns: Elements of Reusable Object-Oriented Software. O livro tornou-se a referência definitiva sobre o tema.

A obra apresenta um catálogo de 23 padrões, organizados em duas dimensões:

  • Escopo: padrões de classe (relacionados à herança) vs. padrões de objeto (relacionados à composição).
  • Propósito: criação, estrutura ou comportamento.

O impacto foi imediato. Pela primeira vez, arquitetos e desenvolvedores passaram a compartilhar um vocabulário comum. Dizer "vamos usar um Observer aqui" substituía longas explicações sobre como desacoplar objetos. O livro padronizou a comunicação técnica e elevou o nível de abstração no projeto de software.

4. Classificação dos padrões segundo o GoF

Padrões Criacionais

Abstraem o processo de instanciação, tornando o sistema independente de como seus objetos são criados.

Exemplo: Singleton

Problema: Garantir que uma classe tenha apenas uma instância e fornecer um ponto global de acesso.
Solução: O construtor é privado. Um método estático retorna a única instância.

Aplicação: Gerenciadores de configuração, pools de conexão.

Exemplo: Factory Method

Problema: Uma classe não pode antecipar a classe de objetos que deve criar.
Solução: Definir uma interface para criar objetos, mas permitir que subclasses decidam qual classe instanciar.

Aplicação: Frameworks que precisam criar componentes específicos sem conhecer suas classes concretas.

Padrões Estruturais

Compoem classes e objetos em estruturas maiores, mantendo flexibilidade e eficiência.

Exemplo: Adapter

Problema: Duas classes com interfaces incompatíveis precisam trabalhar juntas.
Solução: Um adaptador converte a interface de uma classe em outra interface esperada pelo cliente.

Aplicação: Integração de bibliotecas legadas com novos sistemas.

Exemplo: Composite

Problema: Tratar objetos individuais e composições de objetos de maneira uniforme.
Solução: Uma estrutura de árvore onde folhas e nós compartilham a mesma interface.

Aplicação: Sistemas de arquivos, interfaces gráficas com containers e componentes.

Padrões Comportamentais

Definem algoritmos e a distribuição de responsabilidades entre objetos.

Exemplo: Observer

Problema: Um objeto precisa notificar vários outros sobre mudanças em seu estado, sem acoplá-los.
Solução: O sujeito mantém uma lista de observadores e os notifica automaticamente.

Aplicação: Eventos em interfaces gráficas, sistemas de notificação.

Exemplo: Strategy

Problema: Diferentes algoritmos para uma mesma tarefa precisam ser intercambiáveis.
Solução: Definir uma família de algoritmos encapsulados em classes separadas com uma interface comum.

Aplicação: Algoritmos de ordenação, cálculos de impostos, validações.

5. Além do GoF: outras famílias de padrões

Os padrões GoF tratam do nível de classes e objetos. Mas existem outras categorias importantes:

  • Padrões de Arquitetura: MVC, Layers, Microservices, Event-Driven. Focam na estrutura do sistema como um todo.
  • Padrões de Integração: Enterprise Integration Patterns (Hohpe & Woolf, 2003) — como Message Channel, Router, Transformer.
  • Padrões de Concorrência e Distribuição: Active Object, Reactor, Half-Sync/Half-Async (Schmidt et al.).

Cada família atende a um nível diferente de abstração e escopo.

6. Como um Design Pattern se relaciona com coesão e acoplamento

Padrões são ferramentas poderosas para melhorar a qualidade do design. Eles promovem:

  • Alta coesão: O padrão Strategy agrupa algoritmos relacionados em classes separadas, cada uma com uma responsabilidade única.
  • Baixo acoplamento: O padrão Observer desacopla o sujeito dos observadores, permitindo que novos observadores sejam adicionados sem modificar o sujeito.

O padrão Facade exemplifica a Lei de Demeter: ele fornece uma interface simplificada para um subsistema complexo, reduzindo o acoplamento entre o cliente e os componentes internos.

Cuidado com anti-patterns: Aplicar um padrão sem necessidade pode gerar problemas. Por exemplo, um Singleton mal utilizado pode se tornar uma God Class disfarçada, violando o princípio da responsabilidade única.

7. Críticas e evolução dos Design Patterns

Em 1996, Peter Norvig argumentou que muitos padrões GoF são "remédios para linguagens pobres". Em linguagens como Lisp ou Smalltalk, recursos como closures, funções de primeira classe e metaprogramação tornam alguns padrões desnecessários.

Com o tempo, linguagens modernas incorporaram esses recursos:

  • Closures e lambdas reduzem a necessidade de Strategy e Command.
  • Traits e mixins simplificam padrões como Decorator.
  • Injeção de dependência automatiza o que Factory Method e Abstract Factory faziam manualmente.

O futuro dos Design Patterns não é o desaparecimento, mas a evolução. Eles continuam sendo uma linguagem viva para descrever soluções arquiteturais. O mais importante é conhecer o problema, não apenas decorar a solução. Um padrão só é útil quando aplicado no contexto certo, com compreensão de seus trade-offs.


Referências