Open-Closed Principle: Restaurante Sistema Implacável
Sistema de restaurante quebra quando adiciona novo prato? OCP resolve isso sem modificar código existente. Matemática severa da extensibilidade.
Por que isso é importante
Sistema de restaurante que quebra a cada novo prato custa R$ 15K+ mensais em bugs. Desenvolvedor sênior ganha 40% mais dominando OCP vs júnior que modifica código existente.
Você adiciona um novo prato no cardápio e o sistema inteiro quebra. Modifica desconto de pizza e hambúrguer para de funcionar. Open-Closed Principle resolve essa matemática venenosa da extensibilidade.
Problema Real: Sistema Frágil
Restaurante precisa calcular preços diferentes por tipo de prato. Pizza tem taxa de forno, hambúrguer tem desconto horário, salada tem adicional orgânico.
❌ Código Ruim (Viola OCP)
function calcularPreco(prato: any): number { let preco = prato.precoBase; if (prato.tipo === 'pizza') { preco += 5; // Taxa do forno } else if (prato.tipo === 'hamburguer') { if (isHorarioPromocional()) { preco *= 0.8; // 20% desconto } } else if (prato.tipo === 'salada') { preco += 3; // Adicional orgânico } return preco; }
Novo prato = modificar função existente
Mudança em pizza pode quebrar hambúrguer
Testes quebram constantemente
Open-Closed Principle: Definição Precisa
Princípio de Ouro
Módulo deve estar aberto para extensão, mas fechado para modificação.
Traduzindo: você adiciona funcionalidade nova sem alterar código que já funciona.
✅ Aberto para Extensão
Pode adicionar novo comportamento facilmente
🔒 Fechado para Modificação
Não altera código fonte existente
Solução: Polimorfismo Estratégico
Orientação a objetos com herança resolve a matemática da extensibilidade. Cada tipo de prato vira uma classe independente.
✅ Classe Abstrata Base
abstract class Prato { constructor( protected nome: string, protected precoBase: number ) {} abstract calcularPreco(): number; abstract getDescricao(): string; }
🍕 Implementação Pizza
class Pizza extends Prato { calcularPreco(): number { return this.precoBase + 5; // Taxa do forno } getDescricao(): string { return `${this.nome} - Pizza artesanal`; } }
🍔 Implementação Hambúrguer
class Hamburguer extends Prato { calcularPreco(): number { let preco = this.precoBase; if (this.isHorarioPromocional()) { preco *= 0.8; // 20% desconto } return preco; } private isHorarioPromocional(): boolean { const hora = new Date().getHours(); return hora >= 14 && hora <= 17; // 14h às 17h } getDescricao(): string { return `${this.nome} - Hambúrguer gourmet`; } }
Extensão Sem Modificação
Restaurante decide adicionar sobremesas no cardápio. Com OCP, você não toca em pizza nem hambúrguer.
🍰 Nova Classe - Sobremesa
class Sobremesa extends Prato { constructor( nome: string, precoBase: number, private temCobertura: boolean = false ) { super(nome, precoBase); } calcularPreco(): number { let preco = this.precoBase; if (this.temCobertura) { preco += 4; // Adicional cobertura } return preco; } getDescricao(): string { const cobertura = this.temCobertura ? 'com cobertura' : 'simples'; return `${this.nome} - Sobremesa ${cobertura}`; } }
Resultado Poderoso
Uso Prático do Sistema
💼 Código Cliente
// Sistema do restaurante const cardapio: Prato[] = [ new Pizza('Margherita', 25), new Hamburguer('Big Burger', 18), new Sobremesa('Torta de Chocolate', 12, true), new Sobremesa('Pudim', 8, false) ]; // Calculadora de pedido function calcularPedido(pratos: Prato[]): void { let total = 0; pratos.forEach(prato => { const preco = prato.calcularPreco(); console.log(`${prato.getDescricao()}: R$ ${preco}`); total += preco; }); console.log(`Total: R$ ${total}`); } calcularPedido(cardapio);
Output do Sistema
Margherita - Pizza artesanal: R$ 30 Big Burger - Hambúrguer gourmet: R$ 14.4 Torta de Chocolate - Sobremesa com cobertura: R$ 16 Pudim - Sobremesa simples: R$ 8 Total: R$ 68.4
Vantagens Técnicas Severas
🧪 Testabilidade
Cada classe testada isoladamente sem interferência
🔧 Manutenibilidade
Bug em pizza não afeta hambúrguer
📈 Escalabilidade
Adicionar 50 tipos novos sem risco
⚡ Performance
Polimorfismo otimizado pela JIT
Casos de Uso Avançados
OCP não se limita a cálculo de preços. Aplicação se estende para qualquer comportamento extensível.
Sistemas de pagamento: PIX, cartão, boleto
Notificações: email, SMS, push, WhatsApp
Validações: CPF, CNPJ, email, telefone
Exemplo Sistema Pagamento
abstract class ProcessadorPagamento { abstract processar(valor: number): Promise<boolean>; } class PagamentoPix extends ProcessadorPagamento { async processar(valor: number): Promise<boolean> { // Lógica específica do PIX return true; } } class PagamentoCartao extends ProcessadorPagamento { async processar(valor: number): Promise<boolean> { // Lógica específica do cartão return true; } }
Implementação Step-by-Step
Identifique variação de comportamento
Onde você tem if/else ou switch extensos
Crie classe abstrata base
Defina contrato comum com métodos abstratos
Implemente classes concretas
Uma para cada variação de comportamento
Use polimorfismo no cliente
Trabalhe com tipo abstrato, não implementações
Teste extensibilidade
Adicione nova implementação sem modificar existente
Checklist Open-Closed Principle
Próximos Passos
OCP é apenas um dos 5 princípios SOLID. Domine todos para escrever código que escala sem quebrar.
📚 Estude os Outros Princípios
Single Responsibility, Liskov Substitution, Interface Segregation, Dependency Inversion
🛠️ Pratique com Projetos Reais
Implemente em sistema de e-commerce, blog, API REST