Padrões de Design em TypeScript: da teoria à prática
Padrões de Design (Design Patterns) são soluções comprovadas para problemas típicos no desenvolvimento de software. No contexto do TypeScript, com seu poderoso sistema de tipos e suporte a OOP, muitos padrões clássicos do livro "Gang of Four" (GoF) ganham uma implementação nova e mais elegante. Neste artigo, vamos analisar três padrões-chave com exemplos reais em TypeScript que ajudarão você a escrever um código mais limpo, escalável e de fácil manutenção.
1. Padrão Criacional: Singleton
Finalidade: Garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global a ela.
Em TypeScript, a implementação do Singleton é particularmente conveniente graças às propriedades estáticas e construtores privados. Este padrão é ideal para gerenciar conexões de banco de dados, loggers ou caches.
class DatabaseConnection { private static instance: DatabaseConnection; private connectionString: string;
private constructor(connectionString: string) { this.connectionString = connectionString; console.log(`Conexão criada para: ${connectionString}`); }
public static getInstance(connectionString?: string): DatabaseConnection { if (!DatabaseConnection.instance) { if (!connectionString) { throw new Error("Connection string required for first initialization"); } DatabaseConnection.instance = new DatabaseConnection(connectionString); } return DatabaseConnection.instance; }
public query(sql: string): void { console.log(`Executando consulta: ${sql} em ${this.connectionString}`); }}
// Usoconst db1 = DatabaseConnection.getInstance("postgres://localhost:5432/mydb");const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true — mesmo objetodb1.query("SELECT * FROM users");Importante: Em TypeScript, você pode usar readonly e genéricos para implementações mais flexíveis. No entanto, lembre-se de que o Singleton pode dificultar os testes — considere a injeção de dependência (DI) como alternativa.
2. Padrão Estrutural: Decorator
Finalidade: Adiciona dinamicamente novas responsabilidades a um objeto sem alterar sua estrutura. Em TypeScript, decorators são um recurso de primeira classe da linguagem (experimental no ES5, mas amplamente usado em Angular e NestJS).
Vamos considerar um exemplo com um decorator para registrar chamadas de métodos:
// Interface alvointerface ILogger { log(message: string): void;}
// Classe baseclass SimpleLogger implements ILogger { public log(message: string): void { console.log(`[LOG]: ${message}`); }}
// Decorator (classe wrapper)class TimestampDecorator implements ILogger { private logger: ILogger;
constructor(logger: ILogger) { this.logger = logger; }
public log(message: string): void { const timestamp = new Date().toISOString(); this.logger.log(`[${timestamp}] ${message}`); }}
// Decorator para formataçãoclass UpperCaseDecorator implements ILogger { private logger: ILogger;
constructor(logger: ILogger) { this.logger = logger; }
public log(message: string): void { this.logger.log(message.toUpperCase()); }}
// Usolet logger: ILogger = new SimpleLogger();logger.log("Olá mundo"); // [LOG]: Olá mundo
logger = new TimestampDecorator(logger);logger = new UpperCaseDecorator(logger);logger.log("decorators funcionam"); // [LOG]: [2025-01-15T10:30:00.000Z] DECORATORS FUNCIONAMDica: Em TypeScript, também estão disponíveis decorators de classe e propriedade (sintaxe @decorator