Singleton em JS

Leia em: 6 minutos Foto de Saulo Santiago Escrito por: Saulo Santiago

Singleton é um design pattern.

Este padrão fica responsável por criar um objeto e garantir uma única instância de sua classe.

Um conceito principal para implementação de um objeto singleton é que precisamos pensar em um forma de disponibilizarmos uma propriedade no objeto de maneira que possamos acessa-lo direto sem a necessidade de uma instância.

Implementação no ES5

Vou mostrar uma implementação básica em javascript na versão do ECMA 5:

window.Foo = (function() {
  var _instance = void 0;

  Foo.getInstance = function() {
    return !!_instance ? _instance : _instance = new Foo();
  };

  function Foo() {
    console.log('new instance');
  }

  return Foo;
})();

Foo.getInstance() // => new instance
Foo.getInstance() // => return object instantied

De início declarei uma variável interna chamada _instance. A função desta variável é guardar o estado da instância enquanto nosso script estiver sendo executado.

Logo após foi definido uma propriedade chamada de getInstance em Foo que tem como valor uma função que executará nossa regra de singleton. Esta função pode ser chamada direto através do objeto Foo.

A primeira e única instrução que encontramos dentro da nossa propriedade getInstance é um ternário que verifica o estado da variável _instance.

Quando a variável estiver nula significa que estamos chamando a propriedade getInstance pela primeira vez, então o seguinte trecho de código é executado: _instance = new Foo() (uma nova instância de Foo é gerado e armazenado na variável _instance).

Das próximas n vezes que chamarmos a propriedade getInstance através do objeto Foo ja teremos a instância do objeto armazenada na variável getInstance então é feito apenas o retorno da variável que guarda a instância do objeto Foo.

Implementação no ES6

Para finalizar irei mostrar um exemplo de singleton utilizando o ECMA 6:

let _instance = Symbol();
let _singletonEnforcer = Symbol();

class Foo {
  constructor(enforce) {
    console.log('new instance');
    if (enforce !== _singletonEnforcer) {
      throw('Cannot constructor singleton')
    }
  }

  static get getInstance() {
    if(!this[_instance]) {
      this[_instance] = new Foo(_singletonEnforcer);
    }

    return this[_instance];
  }
}

Foo.getInstance // => new instance
Foo.getInstance // => return object instantied

Esta lógica é um pouco diferente da anterior.

Neste caso criei duas variável fora do escopo da classe, com o nome de _instance e uma outra chamada de _singletonEnforcer ambas definidas como símbolo pois a principal característica dos símbolos é serem únicos e imutáveis, e isto é o suficiente para criamos propriedades na nossa classe que seja única.

Ao criar a classe Foo defini um método estático chamado getInstance que será responsável por controlar se há necessidade de uma nova instância ou se simplesmente devolve a instância já criada.

Note que quando chamamos o método getInstance a primeira coisa que é verificada é se existe uma propriedade definida que não esteja nula !this[_instance]. Caso o valor seja null ele criar esta propriedade na classe Foo e atribui como valor a instância da própria classe Foo: this[_instance] = new Foo(_singletonEnforcer);, caso o valor da propriedade não seja null ele passa para próxima instrução.

A próxima instrução retorna essa propriedade: return this[_instance].

Perceba que ao instanciar a classe Foo eu mando de parâmetro no construtor a váriavel _singletonEnforcer e dentro do construtor da classe eu verifico se o parâmetro que chega é o mesmo valor de _singletonEnforcer, caso seja um valor diferente eu disparo uma exception pois terei certeza de que a instância não foi definida de dentro da função getInstance.

Exemplo de uso

Imagine um objeto que tem a função de abrir um conexão com o banco de dados antes de executar qualquer operação.

Se a cada operação abrirmos uma conexão, com certeza perderemos muito a qualidade de processamente, podendo até travar as conexões.

Mas se utilizarmos uma classe singleton para fazer esta conexão, evitaremos essa sobrecarga pois estaremos utilizando da mesma instância para todas as operações.

O conceito de singleton pode ser aplicado em várias outras lógicas de programação.

Comentários:

Deixe sua dúvida, sugestão ou crítica, estou ancioso para saber tudo que você achou sobre este post: