Design patterns: Singleton

Posted on November 16, 2009

0


Todo mundo sabe dizer alguma coisa sobre Singletons. Singleton é ruim, singleton é anti-pattern. Mas como implemento um em Ruby e por que não deveria utilizá-lo?

O próprio Rails os utiliza por exemplo no ActiveSupport e em algumas tarefas do Rake. A motivação de um singleton está no fato de que algumas coisas são únicas.

Um singleton geralmente deixa pra própria classe gerenciar a criação do objeto. Antes de chegar na implementação do singleton vamos ver alguns comportamentos do ruby.

Uma variável com um ‘@’ é uma váriável de instância. Já uma variável com duas arrobas é uma variável de classe. Um método em ruby é da classe caso tenha o ‘self’ antes.

Picture 1

Podemos criar uma instância e incrementar tanto a variável de classe como a variável de instância.
Ao instanciar uma segunda vez, a variável de classe se mantém e a de instância é obviamente ‘zerada’.

Picture 2

Utilizando a idéia acima podemos criar uma classe simples de log, onde desejamos que apenas uma seja de fato criada. Para isso definiremos o método ‘instance’ como método de classe que sempre retorna a mesma instancia da classe, guardada pela variável de classe “@@instance”

Picture 3

Quase funcionando, porém ainda poderíamos instanciar a classe de log…Pra evitar isso podemos usar o metodo ‘private_class_method’ passado como parâmetro méthodo ‘new’.

Picture 4

A implementação do singleton está completa. A classe cria apenas uma instância dela mesma, não permitindo nunca a criação de uma segunda.

Mas..e se fosse necessário mais um singleton na aplicação ? Teríamos que repetir esse processo inteiro.

O ruby fornece uma alternativa a isso que é incluir o módulo ‘singleton’.

Picture 5

Uma chamada a Logger.instance devolve a uma única intância sempre da mesma maneira que a implementação anterior.

eager instantiation x lazy

Existe uma diferença entre o singleton que construímos e o módulo singleton do ruby.

Na nossa implementação do singleton a instância é criada antes de qualquer chamada a Logger.instance. Criar uma instancia antes mesmo antes que ninguém necessite é chamado de ‘eager instantiation’. Já a forma utilizada no módulo singleton espera pela primeira invocação para criar a instância, essa chamada é a conhecida como ‘lazy instatiation’.

Variáveis globais x Singletons

Podemos, por exemplo, utilizar uma variável global como singleton. E é aqui que o negócio começa a ficar feio. Em Ruby, qualquer variável iniciada com ‘$’ é global, você pode acessar de qualquer lugar. Portanto essa poderia ser uma boa implementação para singletons.

NÃO. Não existe nada que controle o calor da variável global nese caso como fizemos com o singleton, além das variáveis globais serem impossíveis de serem criadas antes(eager instatiation). Além disso não existem nada que garanta que essa variável não seja recriada e substituída em outro lugar.

Picture 6

É por isso que em ruby um singleton é implementado como um módulo. Como não é possível implementar um módulo seus métodos claramentes são criados para serem chamados sem permitir a criação de um objeto.

Picture 7

Agora nunca mais teremos mais de uma instância da classe Gerenciador. Ainda não… Ainda seria possível utilizar o método clone do Ruby.

Picture 8

Não que isso seja uma boa idéia, mas mostra como Ruby é uma linguagem onde praticamente tudo pode ser mudado em tempo de execução. A filosofia do Ruby é que se você decide que realmente precisa de uma implementação diferenta da craida anteriormente, a linguagem e permitirá isso, fica a seu critério e a sua responsabilidade.

homealone

Singleton: Anti-pattern

Um singleton sempre será basicamente uma variável única de escopo global. Com isso você cria um canal de comunicação direto entre qualquer classe com o singleton, deixando totalmente acomplado o relacionamento entre eles.

Outro problema é a dificuldade de se fazer testes unitários quando sua implementação se relaciona com um singleton. Como o estado pode sempre variar, seus testes terão ganho uma complexidade maior do que a necessária.

A recomendação então é: Não use singleton! Singletons propriamente aplicados são executados apenas uma vez, mas mesmo assim, não use singleton!

Posted in: rails, ruby