Singleton


O que é?

  • Um design pattern que possui essas principais características:
    • Restrige a somente uma instância de uma classe
    • A instância só é criada ao ser chamada explicitamente
    • Não passa parâmetros na criação (método construtor)
    • Objeto pode ser acessada globalmente para toda aplicação
  • Normalmente o método construtor é privado
  • Pode limitar o acesso simultâneo a um recurso compartilhado
  • Possui um método chamado getInstance
    • Responsável na criação do objeto
    • Retorna o novo objeto (caso for a primeira instância)
    • Se a classe já tenha sido instanciada, é retornado o objeto existente

Casos de Uso

  • Três casos mais comuns de uso desse design pattern:
  1. Sistema de logs
    • Sempre jogando os logs no mesmo objeto
  2. Conexão única ao banco de dados
    • Instância única que possui acesso ao banco de dados
    • Garante que não haja novas instâncias
    • Assim, impedindo que novas conexões sejam estabelecidas
  3. Acesso ao sistema de arquivos de um sistema
    • Criando ponto único de acesso para modificação de arquivos
    • Impedindo alteração simultânea de um arquivo

Exemplo de código

counter.js
let instance;
let counter = 0;
 
class Counter {
  constructor() {
    if (instance) {
      throw new Error("You can only create one instance!");
    }
    instance = this;
  }
 
  getInstance() {
    return this;
  }
 
  getCount() {
    return counter;
  }
 
  increment() {
    return ++counter;
  }
 
  decrement() {
    return --counter;
  }
}
 
const singletonCounter = Object.freeze(new Counter());
export default singletonCounter;

Por que anti-pattern?

  • Listo abaixo os 5 motivos pelos quais não utilizar esse pattern
  • Pelo menos em sua implementação mais simplória
  1. Não é Thread safe
    • Não foi pensada em casos como Paralelismo, Concorrência, Sistemas Distribuídos
    • Pois se dois contextos diferentes interagirem com o objeto, bugs poderam ocorrer
    • Porque o objeto não é protegido (lock) quando modificado por um contexto (thread)
  2. Alto acoplamento
    • Deve-se sempre chamar o métodogetInstance para ter acesso ao meu objeto
    • Criando alto acomplamento, algo que na POO não é visado!
  3. Acesso global de um objeto
    • Tendo acesso em qualquer parte da minha aplicação
    • Se eu modificar as propriedades do objeto em um canto da app
    • Eu modifico ela globalmente, em minha app inteira
  4. Dificuldade ao trabalhar com testes
    • De forma unitária, deveriámos criar novas instâncias, não reutilizar uma instância já existente
  5. SOLID não é respeitado
    • A classe nesse pattern fere a responsabilidade única (SRP)
    • Pois ela fica responsável pelo seu “motivo de criação”
    • E pelo seu auto-gerenciamento, impedindo a criação de novas instâncias