Introdução: Por que SOLID é importante para o desenvolvedor TypeScript
SOLID — são cinco princípios fundamentais da programação orientada a objetos, formulados por Robert Martin (Tio Bob). No contexto do TypeScript, que combina tipagem estática e flexibilidade do JavaScript, a aplicação dos princípios SOLID permite criar aplicações escaláveis, testáveis e de fácil manutenção. Neste artigo, analisaremos cada princípio com base teórica e exemplos práticos em TypeScript.
1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única
Teoria
Uma classe deve ter apenas um motivo para mudar. Isso significa que cada módulo, classe ou função é responsável por exatamente uma funcionalidade.
Prática em TypeScript
Vamos considerar a violação do SRP no exemplo de uma classe que gerencia usuários e envio de notificações:
// ❌ Violação do SRPclass UserManager { constructor(private name: string, private email: string) {}
saveUser(): void { console.log(`Usuário ${this.name} salvo no BD`); }
sendWelcomeEmail(): void { console.log(`E-mail de boas-vindas enviado para ${this.email}`); }}Versão corrigida com separação de responsabilidades:
// ✅ Cumprimento do SRPclass User { constructor(public name: string, public email: string) {}}
class UserRepository { save(user: User): void { console.log(`Usuário ${user.name} salvo no BD`); }}
class EmailService { sendWelcomeEmail(user: User): void { console.log(`E-mail de boas-vindas enviado para ${user.email}`); }}2. Open/Closed Principle (OCP) — Princípio Aberto/Fechado
Teoria
Entidades de software devem estar abertas para extensão, mas fechadas para modificação. Em outras palavras, você deve ser capaz de adicionar nova funcionalidade sem alterar o código existente.
Prática em TypeScript
Exemplo com cálculo de descontos para diferentes tipos de clientes:
// ❌ Violação do OCPclass DiscountCalculator { calculateDiscount(customerType: string, amount: number): number { if (customerType === 'regular') { return amount * 0.1; } else if (customerType === 'premium') { return amount * 0.2; } else if (customerType === 'vip') { return amount * 0.3; } return 0; }}// ✅ Cumprimento do OCP através de polimorfismointerface DiscountStrategy { calculate(amount: number): number;}
class RegularDiscount implements DiscountStrategy { calculate(amount: number): number { return amount * 0.1; }}
class PremiumDiscount implements DiscountStrategy { calculate(amount: number): number { return amount * 0.2; }}
class DiscountCalculator { constructor(private strategy: DiscountStrategy) {}
calculate(amount: number): number { return this.strategy.calculate(amount); }}3. Liskov Substitution Principle (LSP) — Princípio da Substituição de Barbara Liskov
Teoria
Objetos em um programa devem ser substituíveis por instâncias de seus subtipos sem alterar a correção do programa. Em palavras simples: se você tem uma classe base, qualquer herdeiro dela deve funcionar corretamente em todos os lugares onde a classe base é usada.
Prática em TypeScript
Exemplo clássico com figuras geométricas:
// ❌ Violação do LSPclass Rectangle { constructor(protected width: number, protected height: number) {}
setWidth(width: number): void { this.width = width; }
setHeight(height: number): void { this.height = height; }
getArea(): number { return this.width * this.height; }}
class Square extends Rectangle { setWidth(width: number): void { this.width = width; this.height = width; // Violação! }
setHeight(height: number): vo