Desenvolvimento

    O Que São Design Patterns: Catálogo GoF, Categorias e Aplicação Moderna

    Entenda o que são design patterns, a origem do catálogo GoF de 1994, as 3 famílias criacional, estrutural e comportamental, e exemplos práticos em TypeScript.

    2026-10-0713 minEquipe MaxVision
    CLIP_001 · DJI O4FPV · 4K · 60FPS

    Design patterns (padrões de projeto) são soluções arquiteturais reutilizáveis para problemas recorrentes no desenvolvimento de software orientado a objetos e modular. O Gang of Four (GoF) formalizou 23 padrões em 1994. Eles dividem-se em três famílias: criacionais, estruturais e comportamentais, garantindo manutenibilidade e desacoplamento sem impor rigidez.

    No desenvolvimento corporativo, equipes frequentemente enfrentam problemas idênticos. Dificuldades para plugar novos meios de pagamento, tratar notificações em tempo real ou isolar bibliotecas legadas repetem-se em quase toda aplicação.

    Em vez de reinventar mecanismos do zero, a engenharia utiliza padrões comprovados. Cada padrão atua como uma planta arquitetural que orienta o design, acelerando o ciclo de entrega.

    Contudo, padrões não são trechos de código para cópia direta. Eles descrevem relações topológicas entre classes, interfaces e funções, cabendo ao desenvolvedor adaptá-los ao contexto do projeto.

    Quando aplicados com parcimônia, os design patterns impedem a degradação estrutural do sistema. Eles garantem que novas funcionalidades entrem em produção sem exigir regravações dolorosas em módulos estáveis.

    Origem Histórica e os Dois Axiomas da Gang of Four

    Os design patterns surgiram na computação com o catálogo GoF em 1994. Seus quatro autores adaptaram conceitos da arquitetura urbana de Christopher Alexander. A arquitetura fundamenta-se em dois axiomas perpétuos: programar voltado para interfaces e favorecer a composição de objetos em detrimento da herança rígida entre classes.

    A raiz epistemológica do movimento nasceu fora da informática. Em 1977, o arquiteto austríaco Christopher Alexander publicou A Pattern Language. Ele demonstrou que soluções funcionais em cidades e edifícios podiam ser catalogadas e reutilizadas com sucesso.

    Uma década depois, Kent Beck e Ward Cunningham transportaram essa abordagem para a programação no OOPSLA 1987. Eles apresentaram uma linguagem preliminar de padrões construída sobre Smalltalk para interfaces de usuário.

    A consolidação definitiva ocorreu em outubro de 1994. A Addison-Wesley lançou Design Patterns: Elements of Reusable Object-Oriented Software. A obra catalogou 23 padrões a partir de frameworks em C++ e Smalltalk.

    Para evitar que os padrões se transformassem em receitas burocráticas, a Gang of Four estabeleceu duas regras basilares:

    1. Programe para uma interface, não para uma implementação: o código consumidor deve depender de abstrações estáveis, nunca de tipos concretos voláteis;
    2. Favoreça a composição de objetos em relação à herança de classes: a herança gera acoplamento estático em compilação, enquanto a composição permite montar comportamentos dinâmicos em execução.

    Essa filosofia conecta-se organicamente aos princípios do artigo canônico sobre SOLID. Enquanto o SOLID dita o porquê da flexibilidade, os padrões de projeto fornecem o como prático da montagem.

    As Três Famílias Canônicas do Catálogo GoF

    O catálogo GoF organiza 23 padrões em três famílias taxonômicas distintas segundo a finalidade arquitetural. Os padrões criacionais gerenciam a instanciação desacoplada de classes. Os estruturais organizam a composição entre objetos. Já os comportamentais estruturam algoritmos, reatividade e comunicação distribuída entre componentes do sistema.

    A taxonomia foi elaborada para simplificar a identificação de soluções durante o planejamento de software. Em vez de percorrer dezenas de alternativas soltas, o engenheiro consulta a família correspondente à dor estrutural enfrentada.

    A família dos Padrões Criacionais agrupa 5 soluções voltadas para a criação de instâncias. Eles abstraem o processo de inicialização, tornando o sistema independente de como seus objetos são compostos e representados na memória.

    A família dos Padrões Estruturais reúne 7 soluções dedicadas à montagem de classes maiores. Eles utilizam herança e composição de interfaces para garantir que subsistemas complexos possam operar juntos com baixo atrito e contratos limpos.

    A família dos Padrões Comportamentais engloba 11 soluções focadas na delegação de responsabilidades. Eles organizam como classes conversam entre si, distribuindo o fluxo de controle através de mensagens e eventos assíncronos.

    Os 23 Padrões GoF: Panorama Completo e Tabela Comparativa

    A matriz canônica de 23 padrões sintetiza as soluções universais para flexibilidade e manutenibilidade em engenharia de software. Cada padrão resolve uma fragilidade específica de acoplamento. As soluções variam de fábricas abstratas a cadeias de responsabilidade. Times evoluem regras sem quebrar contratos legados já estabilizados em produção.

    Abaixo, a síntese completa do catálogo original da Gang of Four:

    CategoriaPadrãoIntenção ArquiteturalProblema TípicoComplexidade
    CriacionalFactory MethodDelega a criação de instâncias para subclassesAcoplamento ao operador newBaixa
    CriacionalAbstract FactoryCria famílias de objetos relacionados sem especificar classesSuporte a múltiplos ecossistemas visuais ou bancosAlta
    CriacionalBuilderConstrói objetos complexos passo a passo com flexibilidadeConstrutores gigantes com múltiplos parâmetrosMédia
    CriacionalPrototypeClona objetos existentes sem depender de suas classesAlto custo de inicialização de novas instânciasMédia
    CriacionalSingletonGarante instância única com ponto de acesso globalConflito em pools de conexão e logs concorrentesBaixa
    EstruturalAdapterConverte a interface de uma classe em outra compatívelIntegração com SDKs externos heterogêneosBaixa
    EstruturalBridgeSepara abstração de implementação para variá-las livrementeExplosão de subclasses ortogonaisAlta
    EstruturalCompositeCompõe objetos em árvores hierárquicas parte-todoTratamento uniforme de nós e folhas em árvoresMédia
    EstruturalDecoratorAgrega responsabilidades adicionais dinamicamenteExplosão de subclasses para combinações de recursosMédia
    EstruturalFacadeFornece interface simplificada para biblioteca complexaDificuldade de uso de subsistemas densosBaixa
    EstruturalFlyweightCompartilha partes de estado para suportar milhares de objetosEsgotamento de memória RAM por objetos idênticosAlta
    EstruturalProxyControla o acesso a um objeto através de substitutoCaching, autorização tardia e carregamento preguiçosoMédia
    ComportamentalChain of ResponsibilityPassa requisições ao longo de uma esteira de handlersPipeline de validações e middleware sequenciaisMédia
    ComportamentalCommandEncapsula requisição como objeto com suporte a desfazerFilas de tarefas, transações e histórico de açõesMédia
    ComportamentalInterpreterDefine representação gramatical para linguagem específicaAvaliação de filtros dinâmicos e regras de cálculoAlta
    ComportamentalIteratorAcessa elementos de coleção sequencialmente sem expor formatoNavegação agnóstica por listas, árvores e grafosBaixa
    ComportamentalMediatorCentraliza comunicações complexas entre múltiplos objetosAcoplamento cruzado entre dezenas de componentesMédia
    ComportamentalMementoCaptura estado interno sem violar encapsulamentoMecanismos de checkpoint e rollback de operaçõesMédia
    ComportamentalObserverNotifica múltiplos assinantes sobre alterações de estadoReatividade de interface e barramentos de eventosMédia
    ComportamentalStateAltera comportamento quando o estado interno mudaMáquinas de estado finitas e transições de pedidosMédia
    ComportamentalStrategyEncapsula família de algoritmos intercambiáveisEncadeamento de if-else para cálculos de freteBaixa
    ComportamentalTemplate MethodDefine esqueleto de algoritmo deixando passos para subclassesDuplicação de fluxo estrutural com passos variáveisBaixa
    ComportamentalVisitorAdiciona operações a estruturas sem alterar suas classesExportação de documentos para PDF, XML e MarkdownAlta

    Oficina de componentes modulares representando padrões criacionais e injeção de dependência em ambiente de engenharia

    Os Seis Padrões Dominantes na Indústria Moderna

    Na engenharia contemporânea, seis padrões concentram a maior parte das soluções arquiteturais de produção: Factory Method, Singleton, Adapter, Decorator, Strategy e Observer. Eles cobrem da instanciação desacoplada até a comunicação por eventos. Variações de regras de negócio e integrações externas ficam isoladas em módulos autocontidos e testáveis.

    O domínio desses seis modelos atende à maioria dos desafios diários em microsserviços, backends Node.js e aplicações web de grande porte.

    1. Factory Method: Criação Desacoplada de Objetos

    O Factory Method substitui chamadas diretas a construtores por métodos que retornam contratos comuns. Isso permite que módulos consumidores recebam o produto sem conhecer a classe concreta instanciada.

    interface Notifier {
      send(message: string): Promise<void>;
    }
    
    class EmailNotifier implements Notifier {
      async send(message: string): Promise<void> {
        console.log(`Email enviado: ${message}`);
      }
    }
    
    class SmsNotifier implements Notifier {
      async send(message: string): Promise<void> {
        console.log(`SMS enviado: ${message}`);
      }
    }
    
    class NotificationFactory {
      static create(channel: "email" | "sms"): Notifier {
        if (channel === "sms") return new SmsNotifier();
        return new EmailNotifier();
      }
    }
    

    O cliente interage apenas com a interface Notifier. Adicionar canais como mensageria não altera o fluxo central de envio.

    2. Singleton: Quando Faz Sentido e Por Que Evitar

    O Singleton assegura que uma classe possua apenas uma instância global. Ele costuma ser útil para pools de conexão ou configurações imutáveis do ambiente.

    No entanto, o Singleton transformou-se em um dos anti-patterns mais combatidos em testes de software. Manter estado mutável compartilhado acarreta vazamento de dados entre suites paralelas e gera forte acoplamento temporal.

    Em ambientes modernos com módulos ESM no TypeScript, exportar uma constante dispensa classes estáticas. O próprio módulo provê uma instância singleton nativa e previsível.

    3. Adapter: Unificando Contratos Heterogêneos

    O Adapter converte interfaces incompatíveis para que classes com contratos divergentes consigam operar juntas. É indispensável na integração de gateways de pagamento ou SDKs de terceiros.

    interface PaymentGateway {
      charge(amountCents: number): Promise<boolean>;
    }
    
    class LegacyPaymentProvider {
      executeDebit(dollars: number): { status: string } {
        return { status: "PAID" };
      }
    }
    
    class PaymentAdapter implements PaymentGateway {
      constructor(private legacy: LegacyPaymentProvider) {}
    
      async charge(amountCents: number): Promise<boolean> {
        const dollars = amountCents / 100;
        const result = this.legacy.executeDebit(dollars);
        return result.status === "PAID";
      }
    }
    

    Com o adaptador, a camada de checkout permanece limpa e imune a alterações nas assinaturas da biblioteca externa.

    4. Decorator: Agregação Dinâmica sem Subclasses

    O Decorator anexa responsabilidades dinâmicas a objetos existentes envolvendo-os em invólucros (wrappers). Ele evita a explosão de subclasses combinatórias.

    Por exemplo, um serviço de dados pode ser envelopado sequencialmente por um decorador de logging, um decorador de cache e um decorador de métricas. Cada invólucro executa sua regra mantendo a mesma assinatura do contrato original.

    5. Strategy: Algoritmos Intercambiáveis em Tempo de Execução

    O Strategy extrai algoritmos variantes de dentro de classes de negócio e os posiciona em objetos separados. Ele elimina blocos gigantescos de switch-case e respeita diretamente o Princípio Aberto/Fechado (OCP).

    interface ShippingStrategy {
      calculate(weightKg: number): number;
    }
    
    class ExpressShipping implements ShippingStrategy {
      calculate(weightKg: number): number {
        return weightKg * 18.5;
      }
    }
    
    class EconomicShipping implements ShippingStrategy {
      calculate(weightKg: number): number {
        return weightKg * 7.2;
      }
    }
    
    class OrderCheckout {
      constructor(private strategy: ShippingStrategy) {}
    
      getShippingCost(weight: number): number {
        return this.strategy.calculate(weight);
      }
    }
    

    A regra de frete pode ser alterada em tempo de execução sem editar uma única linha da classe OrderCheckout.

    6. Observer: Reatividade e Desacoplamento de Eventos

    O Observer estabelece uma relação de dependência um-para-muitos. Quando o sujeito altera seu estado, todos os seus observadores registrados são notificados instantaneamente.

    Esse padrão forma a fundação de barramentos de eventos modernos, bibliotecas como RxJS e sistemas orientados a microsserviços. O emissor publica o evento sem guardar qualquer conhecimento sobre quem o consumirá.

    Padrões de Projeto vs Anti-patterns: Evitando a Patternitis

    A aplicação prematura de padrões sem uma dor real de manutenção produz a patologia conhecida como patternitis. O design profissional deve emergir da refatoração guiada por dor real. Isso evita indireções que transformam arquiteturas em labirintos de classes difíceis de depurar.

    No ensaio clássico Inversion of Control Containers, Martin Fowler enfatiza que padrões devem solucionar complexidades reais, não especulativas. Tentar antecipar variações que talvez nunca ocorram viola o princípio YAGNI (You Aren't Gonna Need It).

    A tabela a seguir compara o uso produtivo de padrões contra os desvios arquiteturais correspondentes:

    Padrão LegítimoAnti-pattern CorrespondenteSintoma de DegradaçãoRemediação Recomendada
    StrategySwitch HellFunções de 500 linhas com dezenas de case aninhadosExtrair branches para estratégias polimórficas
    SingletonGod Object com Estado GlobalTestes unitários falhando por dados vazados em memóriaInjetar dependências imutáveis via construtor
    Factory MethodOver-engineering prematuroClasses de fábricas gerando apenas um tipo imutávelUsar instanciação direta até haver real variação
    AdapterKludge / Big Ball of MudCódigo legado espalhado por camadas de negócioEncapsular integrações em adaptadores perimetrais
    DecoratorRussian Dolls incontroláveisPilhas de chamadas com 30 níveis de profundidadeLimitar decorators e consolidar responsabilidades
    ObserverEvent Cascade HellLoops infinitos de eventos e travamento de threadsAdicionar rastreamento com identificadores únicos

    Barramento de dados e roteamento de comandos em sistema agêntico com padrões comportamentais

    Design Patterns em TypeScript e Arquiteturas com Agentes de IA

    Em arquiteturas modernas com TypeScript e agentes autônomos, padrões como Adapter e Strategy viabilizam a interoperabilidade de ferramentas e modelos. O orquestrador MaxVision Code usa adapters para normalizar ferramentas e recursos heterogêneos. Em paralelo, strategies selecionam modelos de inferência sem acoplamento a provedores proprietários.

    A especificação do Model Context Protocol (MCP) padronizou a comunicação entre LLMs e ferramentas externas. Ela traduz comandos de terminal, bancos SQL e APIs em contratos padronizados.

    O padrão Adapter é aplicado na esteira de ferramentas para normalizar saídas de comandos e sistemas de arquivos em esquemas JSON previsíveis. Já o padrão Strategy permite chavear provedores de IA conforme métricas de latência e custo por token:

    interface LlmStrategy {
      complete(prompt: string): Promise<string>;
    }
    
    class FastInferenceStrategy implements LlmStrategy {
      async complete(prompt: string): Promise<string> {
        return "Resposta rápida para triagem de código";
      }
    }
    
    class DeepReasoningStrategy implements LlmStrategy {
      async complete(prompt: string): Promise<string> {
        return "Resposta analítica para revisão arquitetural";
      }
    }
    
    class AgentOrchestrator {
      private strategy: LlmStrategy = new FastInferenceStrategy();
    
      setInferenceStrategy(strategy: LlmStrategy): void {
        this.strategy = strategy;
      }
    
      async runTask(task: string): Promise<string> {
        return this.strategy.complete(task);
      }
    }
    

    Essa separação garante total portabilidade. Aplicações conseguem alternar modelos ou hospedar instâncias locais sem reescrever a lógica de ferramentas construída na plataforma.

    Perguntas Frequentes sobre Design Patterns

    O que são design patterns na programação?

    Design patterns (padrões de projeto) são soluções arquiteturais reutilizáveis para problemas frequentes no desenvolvimento de software. Eles fornecem uma estrutura formal para organizar classes, módulos e funções, promovendo código desacoplado e fácil de testar. Não são bibliotecas prontas, mas diretrizes de modelagem estrutural.

    Quem foi a Gang of Four (GoF) e quando o catálogo foi lançado?

    A Gang of Four (GoF) é o apelido dos cientistas da computação Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides. Em 1994, eles publicaram o livro seminal que catalogou 23 padrões clássicos de orientação a objetos. O trabalho tornou-se o principal alicerce da literatura de arquitetura de software global.

    Qual é a diferença entre princípios SOLID e design patterns?

    Os princípios SOLID representam as diretrizes e metas de qualidade que orientam a saúde do código, definindo o porquê de mantermos o sistema desacoplado. Já os design patterns fornecem as soluções táticas e as configurações de classes para atingir essas metas, representando o como prático da implementação.

    Por que o Singleton é considerado um anti-pattern por muitos engenheiros?

    O Singleton torna-se um anti-pattern quando mantém estado mutável compartilhado em escopo global. Esse comportamento impede a execução de testes unitários isolados, gera concorrência imprevisível entre threads e esconde dependências que deveriam ser explícitas no construtor das classes.

    Como saber quando aplicar um padrão sem cair na sobre-engenharia?

    A recomendação técnica consiste em permitir que o padrão emerja a partir de uma refatoração orientada à dor real de manutenção. Se uma classe precisa suportar novos algoritmos ou múltiplos provedores externos, aplique Strategy ou Adapter; se o código não tem variação comprovada, adote a solução mais simples possível.

    Como os design patterns são usados em sistemas com agentes de IA e MCP?

    Em arquiteturas agênticas modernas, o padrão Adapter normaliza APIs, sistemas de arquivos e bancos heterogêneos no formato exigido pelo Model Context Protocol (MCP). O padrão Strategy, por sua vez, permite selecionar dinamicamente entre modelos de raciocínio profundo ou modelos ultrarrápidos dependendo da complexidade da requisição.

    TAGS
    • Design Patterns
    • Padrões de Projeto
    • Gang of Four
    • Arquitetura de Software
    • Engenharia de Software
    • TypeScript
    • Clean Code
    Mascote da MaxVision para contato rápido no WhatsAppFale agora pelo WhatsApp