Padrões de Design em JavaScript com Exemplos: do A ao Z
Padrões de design (design patterns) são soluções comprovadas para problemas típicos que surgem no desenvolvimento de software. Em JavaScript, especialmente no ambiente Node.js, entender esses padrões ajuda a escrever código mais limpo, modular e escalável. Neste artigo, abordaremos as principais categorias de padrões com exemplos práticos.
1. Padrões Criacionais (Creational Patterns)
Esses padrões gerenciam o processo de criação de objetos, tornando o sistema independente da forma como são instanciados.
1.1. Singleton (Singleton)
Garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global a ela. No Node.js, os módulos são armazenados em cache por padrão, o que simplifica a implementação.
class DatabaseConnection { constructor() { if (DatabaseConnection.instance) { return DatabaseConnection.instance; } this.connection = null; DatabaseConnection.instance = this; }
connect() { if (!this.connection) { this.connection = { status: 'connected' }; console.log('Nova conexão com o BD'); } return this.connection; }}
const db1 = new DatabaseConnection();const db2 = new DatabaseConnection();console.log(db1 === db2); // true1.2. Factory Method (Factory Method)
Define uma interface para criar um objeto, mas permite que as subclasses alterem o tipo de objeto criado. Ideal para criar diferentes tipos de loggers.
class LoggerFactory { createLogger(type) { switch (type) { case 'console': return new ConsoleLogger(); case 'file': return new FileLogger(); default: throw new Error('Tipo de logger desconhecido'); } }}
class ConsoleLogger { log(message) { console.log(message); }}
class FileLogger { log(message) { console.log(`[Arquivo]: ${message}`); }}
const factory = new LoggerFactory();const logger = factory.createLogger('console');logger.log('Mensagem de teste');2. Padrões Estruturais (Structural Patterns)
Descrevem maneiras de combinar classes e objetos para criar estruturas maiores.
2.1. Decorator (Decorator)
Adiciona dinamicamente novas responsabilidades a um objeto. Em JavaScript, decorators são frequentemente implementados por meio de funções de alta ordem ou protótipos.
class Coffee { cost() { return 10; } description() { return 'Café'; }}
function withMilk(coffee) { coffee.cost = () => coffee.cost() + 5; coffee.description = () => coffee.description() + ', com leite'; return coffee;}
function withSugar(coffee) { coffee.cost = () => coffee.cost() + 2; coffee.description = () => coffee.description() + ', com açúcar'; return coffee;}
let myCoffee = new Coffee();myCoffee = withMilk(myCoffee);myCoffee = withSugar(myCoffee);console.log(`${myCoffee.description()}: ${myCoffee.cost()} R$`); // Café, com leite, com açúcar: 17 R$2.2. Adapter (Adapter)
Converte a interface de uma classe em outra interface esperada pelo cliente. Particularmente útil ao integrar bibliotecas de terceiros.
class OldAPI { getUserData() { return { name: 'Ivan', age: 25, email: 'ivan@example.com' }; }}
class NewAPI { fetchUser() { return { fullName: 'Ivan Petrov', yearsOld: 26, contact: 'ivan@example.com' }; }}
class UserAdapter { constructor(api) { this.api = api; }
getUserData() { const data = this.api.fetchUser(); return { name: data.fullName, age: data.yearsOld, email: data.contact }; }}
const adapter = new UserAdapter(new NewAPI());console.log(adapter.getUserData()); // { name: 'Ivan Petrov', age: 26, email: 'ivan@example.com' }3. Padrões Comportamentais (Behavioral Patterns)
Definem algorit