O Singleton é um padrão de projeto criacional, que garante que apenas um objeto desse tipo exista e forneça um único ponto de acesso a ele para qualquer outro código.
O Singleton tem quase os mesmos prós e contras que as variáveis globais. Embora sejam super úteis, eles quebram a modularidade do seu código.
Você pode usar classes que dependem de singletons em algumas outras situações. Você terá que levar a classe singleton também. Na maioria das vezes, essa limitação surge durante a criação de testes de unidade.
Exemplos de uso: Muitos desenvolvedores consideram o padrão Singleton um antipadrão. É por isso que seu uso está diminuindo no código Ruby.
Identificação: O Singleton pode ser reconhecido por um método de criação estático, que retorna o mesmo objeto em cache.
Singleton ingênuo
É muito fácil implementar um Singleton desleixado. Você só precisa ocultar o construtor e implementar um método de criação estático.
A mesma classe se comporta incorretamente em um ambiente multithread. Vários threads podem chamar o método de criação simultaneamente e obter várias instâncias da classe Singleton.
main.rb: Exemplo conceitual
# The Singleton class defines the `instance` method that lets clients access the
# unique singleton instance.
class Singleton
@instance = new
private_class_method :new
# The static method that controls the access to the singleton instance.
#
# This implementation let you subclass the Singleton class while keeping just
# one instance of each subclass around.
def self.instance
@instance
end
# Finally, any singleton should define some business logic, which can be
# executed on its instance.
def some_business_logic
# ...
end
end
# The client code.
s1 = Singleton.instance
s2 = Singleton.instance
if s1.equal?(s2)
print 'Singleton works, both variables contain the same instance.'
else
print 'Singleton failed, variables contain different instances.'
end
output.txt: Resultados da execução
Singleton works, both variables contain the same instance.
Singleton seguro para threads
Para corrigir o problema, você deve sincronizar os threads durante a primeira criação do objeto Singleton.
main.rb: Exemplo conceitual
# The Singleton class defines the `intance` method that lets clients access the
# unique singleton instance.
class Singleton
attr_reader :value
@instance_mutex = Mutex.new
private_class_method :new
def initialize(value)
@value = value
end
# The static method that controls the access to the singleton instance.
#
# This implementation let you subclass the Singleton class while keeping just
# one instance of each subclass around.
def self.instance(value)
return @instance if @instance
@instance_mutex.synchronize do
@instance ||= new(value)
end
@instance
end
# Finally, any singleton should define some business logic, which can be
# executed on its instance.
def some_business_logic
# ...
end
end
# @param [String] value
def test_singleton(value)
singleton = Singleton.instance(value)
puts singleton.value
end
# The client code.
puts "If you see the same value, then singleton was reused (yay!)\n"\
"If you see different values, then 2 singletons were created (booo!!)\n\n"\
"RESULT:\n\n"
process1 = Thread.new { test_singleton('FOO') }
process2 = Thread.new { test_singleton('BAR') }
process1.join
process2.join
output.txt: Resultados da execução
If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)
RESULT:
FOO
FOO