Princípios SOLID em TypeScript: teoria e prática de nível avançado
SOLID — são cinco princípios fundamentais de design orientado a objetos que permitem criar código flexível, sustentável e escalável. Em TypeScript, graças à tipagem forte, interfaces e classes abstratas, a implementação desses princípios torna-se especialmente eficaz. Neste artigo, analisaremos cada princípio em nível avançado com exemplos reais em TypeScript.
1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única
Teoria: Cada classe deve ter apenas um motivo para mudar. Em TypeScript, isso significa que o módulo é responsável por exatamente uma funcionalidade.
Prática avançada: Frequentemente, a violação do SRP se manifesta em "objetos divinos" (God Objects). Use composição e delegação.
// ❌ Violação do SRP: a classe é responsável por dados, validação e persistênciaclass UserService { constructor(private user: User) {}
validateEmail(email: string): boolean { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email); }
saveToDatabase(): void { // lógica de persistência }
sendWelcomeEmail(): void { // lógica de envio }}
// ✅ SRP: separando responsabilidadesinterface IEmailValidator { isValid(email: string): boolean;}
interface IUserRepository { save(user: User): Promise<void>;}
interface INotificationService { sendWelcome(user: User): Promise<void>;}
class UserRegistrationService { constructor( private validator: IEmailValidator, private repository: IUserRepository, private notifier: INotificationService ) {}
async register(user: User): Promise<void> { if (!this.validator.isValid(user.email)) { throw new Error('Email inválido'); } await this.repository.save(user); await this.notifier.sendWelcome(user); }}2. Open/Closed Principle (OCP) — Princípio Aberto/Fechado
Teoria: As classes devem estar abertas para extensão, mas fechadas para modificação. Em TypeScript, isso é alcançado por meio de classes abstratas e interfaces.
Prática avançada: Use o padrão "Strategy" (Estratégia) para implementar o OCP.
// ❌ Violação do OCP: ao adicionar um novo tipo de relatório, é necessário modificar a classeclass ReportGenerator { generate(data: any[], type: string): string { if (type === 'csv') { return data.map(row => row.join(',')).join(''); } else if (type === 'json') { return JSON.stringify(data); } // ao adicionar 'xml' — modificamos este método throw new Error('Formato não suportado'); }}
// ✅ OCP: usando polimorfismointerface IReportFormatter { format(data: any[]): string;}
class CsvFormatter implements IReportFormatter { format(data: any[]): string { return data.map(row => row.join(',')).join(''); }}
class JsonFormatter implements IReportFormatter { format(data: any[]): string { return JSON.stringify(data); }}
class XmlFormatter implements IReportFormatter { format(data: any[]): string { // implementação para XML return '<data>...</data>'; }}
class ReportGeneratorOCP { constructor(private formatter: IReportFormatter) {}
generate(data: any[]): string { return this.formatter.format(data); }}
// Uso: estendemos sem modificaçãoconst csvReport = new ReportGeneratorOCP(new CsvFormatter());const jsonReport = new ReportGeneratorOCP(new JsonFormatter());3. Liskov Substitution Principle (LSP) — Princípio da Substituição de Liskov
Teoria: Os objetos em um programa devem ser substituíveis por instâncias de seus subtipos sem alterar a correção do programa. Em TypeScript, isso é especialmente crítico devido à tipagem nominativa.
Blogs
Book Recommendations
Leia também
Programação Assíncrona em Swift: guia completo para iOS/macOS