December 20, 2025 (4mo ago)

Um Guia Prático para Padrões de Projeto OOP

Domine padrões de projeto OOP com nosso guia. Aprenda padrões criacionais, estruturais e comportamentais com exemplos de código claros e práticos para elevar suas habilidades.

← Back to blog
Cover Image for Um Guia Prático para Padrões de Projeto OOP

Domine padrões de projeto OOP com nosso guia. Aprenda padrões criacionais, estruturais e comportamentais com exemplos de código claros e práticos para elevar suas habilidades.

Um Guia Prático para Padrões de Projeto OOP

Domine padrões de projeto OOP com nosso guia. Aprenda padrões criacionais, estruturais e comportamentais com exemplos de código claros e práticos para elevar suas habilidades.

Introdução

Padrões de projeto em programação orientada a objetos são roteiros comprovados e reutilizáveis para resolver desafios de design recorrentes. Eles não são código para copiar e colar; são modelos adaptáveis que ajudam a estruturar classes e objetos para que seu código seja mais fácil de manter, estender e testar.

Uma boa compreensão dos padrões comuns—criacionais, estruturais e comportamentais—permite comunicar a intenção de design claramente com colegas, refatorar código legado com segurança e escolher a ferramenta certa para cada problema arquitetural.

O que são padrões de projeto em programação orientada a objetos

Já tentou construir algo complexo sem um plano? Programar sem padrões de projeto pode levar a uma base de código emaranhada, difícil de escalar e manter. Padrões de projeto são como o livro de receitas de um chef master: receitas testadas que você adapta para produzir resultados consistentes e fáceis de manter.

Um esboço de um chapéu de chef deixando cair formas de letras douradas em um livro de receitas aberto.

A linguagem compartilhada dos desenvolvedores

Um dos benefícios mais valiosos dos padrões é um vocabulário compartilhado. Diga “Factory” ou “Singleton” e desenvolvedores experientes entendem imediatamente a intenção e a estrutura em alto nível, o que agiliza a colaboração e as decisões arquiteturais.

“Design Patterns: Elements of Reusable Object-Oriented Software,” publicado em 1994 por Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, catalogou 23 padrões fundamentais que continuam leitura essencial para praticantes de OOP2. Esses padrões se baseiam em trabalhos acadêmicos anteriores que mostram benefícios mensuráveis de produtividade e reutilização em projetos grandes1.

Um padrão de projeto não é um design acabado que pode ser transformado diretamente em código. É uma descrição ou modelo de como resolver um problema que pode ser usado em muitas situações diferentes.

Ficar confortável com princípios centrais de OOP como polimorfismo e herança ajuda a aplicar padrões de forma eficaz. Para um resumo prático, veja nosso guia comparando polimorfismo vs herança.

As três categorias principais de padrões de projeto

O Gang of Four organizou os padrões em três categorias: criacionais, estruturais e comportamentais. Conhecer essas categorias ajuda a escolher rapidamente a abordagem certa para um problema.

Diagrama ilustrando padrões de projeto criacionais, estruturais e comportamentais com ilustrações simples de caixas.

Visão geral das categorias de padrões de projeto OOP

Categoria de PadrãoPropósito PrincipalExemplos Comuns
CriacionalGerenciar e abstrair a criação de objetosFactory, Builder, Singleton, Prototype
EstruturalCompor classes e objetos em estruturas maiores e flexíveisAdapter, Decorator, Facade, Composite
ComportamentalDefinir como objetos interagem e se comunicamObserver, Strategy, Command, Iterator

Padrões criacionais: os especialistas em construção

Padrões criacionais controlam como objetos são criados para que o código cliente não fique fortemente acoplado a classes concretas. Eles ocultam a lógica de criação, aumentam a flexibilidade e permitem gerenciar instanciação (por exemplo, forçando uma única instância com Singleton).

Padrões estruturais: a cola arquitetural

Padrões estruturais ajudam a montar objetos em sistemas maiores. Eles simplificam relações entre componentes para que você possa alterar partes sem quebrar todo o sistema. Padrões como Adapter permitem que interfaces incompatíveis cooperem, o que é inestimável ao integrar bibliotecas de terceiros.

Padrões estruturais simplificam o design do sistema ao identificar maneiras simples de realizar relações entre entidades.

Padrões comportamentais: os diretores da comunicação

Padrões comportamentais regem a interação e responsabilidade entre objetos. Eles criam canais de comunicação limpos—Observer para notificações dirigidas por eventos e Strategy para trocar algoritmos sem modificar clientes.

Ao gerenciar cuidadosamente os caminhos de comunicação, você reduz dependências e torna a base de código mais fácil de entender e manter.

Criando objetos com padrões criacionais

Padrões criacionais introduzem uma camada de abstração em torno da criação de objetos, para que você possa trocar implementações sem alterar o código cliente. Abaixo estão dois exemplos práticos em TypeScript: Singleton e Factory Method.

Uma ilustração contrastando objetos individuais semelhantes a ovos com uma linha de montagem de fábrica produzindo itens coloridos.

O padrão Singleton: garantindo uma instância verdadeira

Singleton garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global. É útil para recursos compartilhados como uma conexão de banco de dados ou logger, mas pode introduzir estado global que complica testes e gerenciamento de dependências3.

Exemplo: um Singleton em TypeScript para uma conexão de banco de dados.

class DatabaseConnection {
  private static instance: DatabaseConnection;

  private constructor() {
    // A private constructor prevents external 'new' calls
    console.log("Connecting to the database...");
  }

  public static getInstance(): DatabaseConnection {
    if (!DatabaseConnection.instance) {
      DatabaseConnection.instance = new DatabaseConnection();
    }
    return DatabaseConnection.instance;
  }

  public query(sql: string): void {
    console.log(`Executing query: ${sql}`);
  }
}

// Usage
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();

db1.query("SELECT * FROM users");
console.log(db1 === db2); // true

A troca do Singleton: use Singletons para recursos verdadeiramente globais e prefira injeção de dependência para melhor testabilidade e modularidade3.

Factory Method: deixando subclasses decidirem o que criar

Factory Method define uma interface para criar um objeto, enquanto subclasses decidem qual produto concreto instanciar. Isso desacopla o código cliente de classes concretas, tornando o sistema mais fácil de estender.

Exemplo: renderizando botões específicos do SO em TypeScript.

interface Button {
  render(): void;
  onClick(f: () => void): void;
}

class WindowsButton implements Button {
  render() { console.log("Rendering a button in Windows style."); }
  onClick(f: () => void) { console.log("Windows button click event."); f(); }
}

class MacButton implements Button {
  render() { console.log("Rendering a button in macOS style."); }
  onClick(f: () => void) { console.log("Mac button click event."); f(); }
}

abstract class Dialog {
  abstract createButton(): Button;

  render() {
    const okButton = this.createButton();
    okButton.render();
  }
}

class WindowsDialog extends Dialog {
  createButton(): Button { return new WindowsButton(); }
}

class MacDialog extends Dialog {
  createButton(): Button { return new MacButton(); }
}

// Client
const os: string = "windows";
let dialog: Dialog;
if (os === "windows") dialog = new WindowsDialog(); else dialog = new MacDialog();
dialog.render();

Factory Method mantém o criador alheio aos produtos concretos, tornando adições como um LinuxDialog diretas.

Construindo sistemas flexíveis com padrões estruturais

Padrões estruturais ajudam a compor objetos em sistemas robustos e adaptáveis. Duas escolhas práticas são Adapter e Decorator.

Ilustrações desenhadas à mão de um adaptador elétrico e uma pilha de anéis representando padrões de projeto OOP.

O padrão Adapter: ligando interfaces incompatíveis

Adapter envolve uma interface incompatível para que ela se conforme ao que seu sistema espera. Isso evita mudanças invasivas em código legado.

Exemplo: adaptando um ModernLogger para uma interface ILogger existente.

class ModernLogger {
  public logInfo(message: string): void {
    console.log(`[INFO]: ${message}`);
  }
}

interface ILogger { log(message: string): void; }

class LoggerAdapter implements ILogger {
  private modernLogger: ModernLogger;
  constructor() { this.modernLogger = new ModernLogger(); }
  public log(message: string): void { this.modernLogger.logInfo(message); }
}

const logger: ILogger = new LoggerAdapter();
logger.log("User logged in successfully.");

O padrão Decorator: adicionando funcionalidade dinamicamente

Decorator adiciona responsabilidades a objetos em tempo de execução envolvendo-os. É mais flexível do que herança e segue o Princípio da Responsabilidade Única.

Exemplo: compondo uma assinatura com complementos opcionais.

interface Subscription { getDescription(): string; getCost(): number; }

class BasicSubscription implements Subscription {
  getDescription(): string { return "Basic Plan"; }
  getCost(): number { return 10; }
}

abstract class SubscriptionDecorator implements Subscription {
  protected subscription: Subscription;
  constructor(subscription: Subscription) { this.subscription = subscription; }
  abstract getDescription(): string;
  abstract getCost(): number;
}

class PremiumSupportDecorator extends SubscriptionDecorator {
  getDescription(): string { return `${this.subscription.getDescription()}, Premium Support`; }
  getCost(): number { return this.subscription.getCost() + 5; }
}

class CloudStorageDecorator extends SubscriptionDecorator {
  getDescription(): string { return `${this.subscription.getDescription()}, 1TB Cloud Storage`; }
  getCost(): number { return this.subscription.getCost() + 7; }
}

let mySubscription: Subscription = new BasicSubscription();
mySubscription = new PremiumSupportDecorator(mySubscription);
mySubscription = new CloudStorageDecorator(mySubscription);
console.log(mySubscription.getDescription());
console.log(mySubscription.getCost());

Essa abordagem composicional mantém os recursos modulares e fáceis de testar.

Gerenciando interações com padrões comportamentais

Padrões comportamentais organizam a comunicação entre objetos para que os sistemas permaneçam flexíveis e fáceis de manter. Dois exemplos centrais são Observer e Strategy.

O padrão Observer: notificando partes interessadas

Observer estabelece uma relação um-para-muitos para que, quando um Subject muda de estado, os Observers sejam notificados automaticamente. É ideal para sistemas dirigidos por eventos.

Exemplo: um serviço simples de notificações.

interface Subject { attach(observer: Observer): void; detach(observer: Observer): void; notify(): void; }
interface Observer { update(subject: Subject): void; }

class NotificationService implements Subject {
  public state: string = '';
  private observers: Observer[] = [];
  attach(observer: Observer): void { this.observers.push(observer); }
  detach(observer: Observer): void { const i = this.observers.indexOf(observer); if (i !== -1) this.observers.splice(i, 1); }
  notify(): void { for (const o of this.observers) o.update(this); }
  public createNewPost(title: string): void { this.state = `New Post: ${title}`; console.log(`\nNotificationService: A new post was created.`); this.notify(); }
}

class EmailNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`EmailNotifier: Sending email about "${subject.state}"`); } }
class PushNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`PushNotifier: Sending push notification for "${subject.state}"`); } }

const notificationService = new NotificationService();
const emailer = new EmailNotifier();
const pusher = new PushNotifier();
notificationService.attach(emailer);
notificationService.attach(pusher);
notificationService.createNewPost("Understanding Observer Pattern");
notificationService.detach(pusher);
notificationService.createNewPost("Why Strategy is Awesome");

Observer produz um design fracamente acoplado: Subjects não precisam conhecer os Observers concretos que notificam.

O padrão Strategy: encapsulando algoritmos

Strategy permite trocar algoritmos em tempo de execução, evitando grandes blocos condicionais. Ele alinha-se com o Princípio Aberto/Fechado ao permitir novas estratégias sem modificar o código existente.4

Exemplo: estratégias de pagamento para um carrinho de compras.

interface PaymentStrategy { pay(amount: number): void; }
class CreditCardStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} with Credit Card.`); } }
class PayPalStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} via PayPal.`); } }

class ShoppingCart {
  private paymentStrategy: PaymentStrategy;
  constructor(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
  public setPaymentStrategy(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
  public checkout(amount: number): void { this.paymentStrategy.pay(amount); }
}

const cart = new ShoppingCart(new CreditCardStrategy());
cart.checkout(150);
cart.setPaymentStrategy(new PayPalStrategy());
cart.checkout(150);

Strategy remove a complexidade condicional e facilita a extensão dos sistemas.

Armadilhas comuns de design e estratégias de refatoração

Escolher um padrão é apenas metade da batalha. Evite anti-padrões como o God Object, que acumula responsabilidades e viola o Princípio da Responsabilidade Única. Procure por cheiros de código—métodos longos, dependências excessivas ou classes monstruosas—e trate-os com refatoração disciplinada.

Refatorar é melhorar a estrutura interna sem alterar o comportamento externo. É assim que você doma sistemas legados com segurança.

Refatorando para o padrão Strategy

Um grande switch ou uma longa cadeia if/else é um cheiro clássico. Refatore extraindo o comportamento variável para uma interface Strategy com classes de estratégia concretas. Substitua o condicional por uma única chamada ao método da estratégia selecionada. Essa mudança melhora extensibilidade, testabilidade e alinhamento com os princípios SOLID4.

Respondendo suas perguntas sobre padrões de projeto OOP

Quantos padrões de projeto devo aprender?

Concentre-se primeiro em 5–7 padrões essenciais: Singleton, Factory, Adapter, Decorator, Observer e Strategy. Aprenda qual problema cada padrão resolve; quando você internaliza isso, aprender padrões adicionais fica muito mais fácil.

Quando devo evitar usar um padrão de projeto?

Evite padrões que adicionam complexidade sem resolver um problema real. Não introduza um padrão “por precaução”. Siga o YAGNI: adicione padrões quando um problema claro e presente os justificar.

Posso usar padrões de projeto com programação funcional?

Sim. Muitos padrões mapeiam naturalmente para técnicas funcionais. Strategy pode ser parâmetros de função, e Decorator pode ser funções de ordem superior. O importante é aplicar o princípio subjacente em vez de copiar rigidamente as formas OOP.


Na Clean Code Guy, ajudamos equipes a construir software que dura incorporando princípios fundamentais em seu fluxo de trabalho. Descubra como nossas auditorias de código e refatoração preparada para IA podem fazer sua equipe entregar com confiança em https://cleancodeguy.com.

1.
W. G. Griswold et al., “A Formal Foundation for Design Pattern Abstraction and Reuse,” Proceedings of ECOOP ’93, University of California, San Diego. https://cseweb.ucsd.edu/~wgg/CSE210/ecoop93-patterns.pdf
2.
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994). https://en.wikipedia.org/wiki/Design_Patterns
3.
Martin Fowler, “Singleton,” Bliki, discusses Singleton trade-offs and testing implications. https://martinfowler.com/bliki/Singleton.html
4.
Robert C. Martin (Uncle Bob), “The SOLID Principles,” which explain Open/Closed and related design goals. https://8thlight.com/blog/uncle-bob/2012/08/13/the-s-o-l-i-d-principles.html
← Back to blog
🙋🏻‍♂️

IA escreve código.
Você faz durar.

Na era da aceleração da IA, código limpo não é apenas uma boa prática — é a diferença entre sistemas que escalam e bases de código que entram em colapso sob seu próprio peso.