<h2>O que são Design Patterns?</h2>
<p>Design Patterns são soluções comprovadas para problemas comuns em desenvolvimento de software. Eles não são código pronto para copiar, mas sim diretrizes que orientam a estrutura e organização do seu projeto. Em JavaScript, os padrões são especialmente valiosos porque a linguagem oferece flexibilidade — talvez até demais. Dominar Factory, Singleton e Builder significa ter ferramentas para criar arquiteturas escaláveis, testáveis e maintíveis.</p>
<p>Estes três padrões pertencem à categoria de padrões criacionais, ou seja, lidam com a forma como os objetos são instanciados. Entender quando e como usá-los é essencial para qualquer desenvolvedor que queira evoluir além de código procedural.</p>
<h2>Factory Pattern</h2>
<h3>Conceito e Aplicação</h3>
<p>O Factory Pattern encapsula a lógica de criação de objetos em uma função ou classe dedicada. Em vez de espalharem <code>new</code> por toda a aplicação, você centraliza como os objetos nascem. Isso torna mudanças futuras mais fáceis e desacopla o código que usa o objeto de sua implementação concreta.</p>
<p>Imagine uma aplicação com diferentes tipos de usuários: Admin, Guest e Premium. Sem Factory, seu código precisaria saber dos detalhes de cada classe. Com Factory, você delega isso:</p>
<pre><code class="language-javascript">// Sem factory (problemático)
let user;
if (type === 'admin') {
user = new Admin(name);
} else if (type === 'guest') {
user = new Guest(name);
}
// Com factory (profissional)
class UserFactory {
static create(type, name) {
switch(type) {
case 'admin':
return new Admin(name, true);
case 'premium':
return new Premium(name, true);
case 'guest':
return new Guest(name, false);
default:
throw new Error(Tipo ${type} desconhecido);
}
}
}
class Admin {
constructor(name, isAdmin) {
this.name = name;
this.isAdmin = isAdmin;
this.permissions = ['read', 'write', 'delete'];
}
}
class Guest {
constructor(name, isAdmin) {
this.name = name;
this.isAdmin = isAdmin;
this.permissions = ['read'];
}
}
// Uso
const admin = UserFactory.create('admin', 'João');
const guest = UserFactory.create('guest', 'Maria');</code></pre>
<p>O benefício é claro: se precisar adicionar um novo tipo de usuário, você só modifica a Factory. Toda a aplicação continua funcionando sem mudanças.</p>
<h2>Singleton Pattern</h2>
<h3>Evitando Múltiplas Instâncias</h3>
<p>Singleton garante que uma classe tenha apenas uma instância em toda a aplicação e fornece um ponto global de acesso a ela. É perfeito para objetos que representam recursos únicos: configurações, loggers, conexões de banco de dados.</p>
<p>O desafio em JavaScript é implementar isso de forma segura, impedindo que alguém acidentalmente crie uma nova instância:</p>
<pre><code class="language-javascript">class Database {
constructor(connectionString) {
// Previne múltiplas instâncias
if (Database.instance) {
return Database.instance;
}
this.connectionString = connectionString;
this.connected = false;
Database.instance = this;
}
connect() {
if (!this.connected) {
console.log(Conectando a ${this.connectionString});
this.connected = true;
}
}
query(sql) {
if (!this.connected) {
throw new Error('Banco não conectado');
}
return Executando: ${sql};
}
}
// Uso
const db1 = new Database('localhost:5432');
const db2 = new Database('other-host:5432');
console.log(db1 === db2); // true — mesma instância!
db1.connect();
console.log(db2.query('SELECT * FROM users')); // Funciona</code></pre>
<p>Uma alternativa moderna e elegante é usar um closure:</p>
<pre><code class="language-javascript">const Logger = (() => {
let instance;
return {
getInstance() {
if (!instance) {
instance = {
logs: [],
log(message) {
this.logs.push(message);
console.log(message);
}
};
}
return instance;
}
};
})();
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
logger1.log('Erro crítico');
console.log(logger1 === logger2); // true</code></pre>
<p>Use Singleton com moderação — dependências globais podem complicar testes unitários. Prefira injeção de dependência quando possível.</p>
<h2>Builder Pattern</h2>
<h3>Construindo Objetos Complexos Passo a Passo</h3>
<p>O Builder Pattern é ideal quando você precisa criar objetos com muitos parâmetros opcionais ou configurações complexas. Em vez de um construtor gigante, você encadeia métodos de configuração, tornando o código legível e flexível.</p>
<p>Considere montar uma requisição HTTP com várias opções:</p>
<pre><code class="language-javascript">class RequestBuilder {
constructor(url) {
this.url = url;
this.method = 'GET';
this.headers = {};
this.body = null;
this.timeout = 5000;
}
setMethod(method) {
this.method = method;
return this; // Retorna this para encadeamento
}
addHeader(key, value) {
this.headers[key] = value;
return this;
}
setBody(body) {
this.body = body;
return this;
}
setTimeout(ms) {
this.timeout = ms;
return this;
}
build() {
return {
url: this.url,
method: this.method,
headers: this.headers,
body: this.body,
timeout: this.timeout
};
}
}
// Uso fluido e intuitivo
const request = new RequestBuilder('https://api.example.com/users')
.setMethod('POST')
.addHeader('Content-Type', 'application/json')
.addHeader('Authorization', 'Bearer token123')
.setBody({ name: 'João', email: 'joao@example.com' })
.setTimeout(10000)
.build();
console.log(request);</code></pre>
<p>O padrão melhora enormemente a legibilidade. Compare: <code>new RequestBuilder('url').setMethod('POST').addHeader(...)</code> versus um construtor com 8 parâmetros posicionais onde você esqueceria qual vem primeiro.</p>
<h2>Quando Usar Cada Padrão</h2>
<p><strong>Factory</strong>: Use quando você precisa criar objetos de diferentes tipos baseado em condições. Exemplos reais: loaders de arquivo (ImageFactory, VideoFactory), criadores de widgets UI, geradores de reportes.</p>
<p><strong>Singleton</strong>: Use para recursos únicos que devem ser acessados globalmente. Exemplos: logger único, pool de conexões, configurações da aplicação, cache central.</p>
<p><strong>Builder</strong>: Use quando o objeto tem muitos parâmetros opcionais ou quando a construção é complexa. Exemplos: configuradores de aplicação, construtores de queries SQL, builders de componentes UI.</p>
<h2>Conclusão</h2>
<p>Design Patterns não são sobre memorizar nomes — são sobre reconhecer problemas e aplicar soluções comprovadas. <strong>Factory resolve o problema de criação variável</strong>, encapsulando lógica condicional. <strong>Singleton garante unicidade</strong>, útil para recursos compartilhados. <strong>Builder torna a construção legível e flexível</strong>, especialmente com muitas opções.</p>
<p>A chave é moderação: use padrões quando agregam valor real, não por usar. Um Singleton desnecessário complica testes. Uma Factory trivial é overhead. Código simples que funciona vence padrões mal aplicados.</p>
<p>Continue praticando, estude padrões estruturais (Decorator, Adapter) e comportamentais (Observer, Strategy) depois. A jornada é gradual, mas cada padrão dominado torna você um desenvolvedor mais capaz.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" target="_blank" rel="noopener noreferrer">MDN Web Docs - Destructuring Assignment</a></li>
<li><a href="https://javascript.info/design-patterns" target="_blank" rel="noopener noreferrer">JavaScript.info - Design Patterns</a></li>
<li><a href="https://refactoring.guru/design-patterns" target="_blank" rel="noopener noreferrer">Refactoring.guru - Design Patterns</a></li>
<li><a href="https://github.com/getify/You-Dont-Know-JS" target="_blank" rel="noopener noreferrer">You Don't Know JS - Scope & Closures</a></li>
<li><a href="https://en.wikipedia.org/wiki/Design_Patterns" target="_blank" rel="noopener noreferrer">Gang of Four - Design Patterns (Livro Clássico)</a></li>
</ul>