Design pattern Singleton (Object)
🎯 Objectif : garantir une instance unique d'un objet
Anti-pattern
On créé une Factory MyClassFactory
pour MyClass
Elle instance avec une memoization une seule instance de MyClass
class MyClass
def initialize
end
end
class MyClassFactory
def MyClassFactory::get(*args, &block)
@myclass ||= MyClass::new(*args, &block)
end
end
p MyClassFactory::get
p MyClassFactory::get
p MyClass::new
p ObjectSpace.each_object(MyClass).count
ℹ️ Remarque : en Ruby une Factory est plus souvent une méthode de classe de la Classe à construire elle-même, qu'une classe dédiée.
Sortie :
#<MyClass:0x0000559b3b3780f8>
#<MyClass:0x0000559b3b3780f8>
#<MyClass:0x0000559b3b373418>
2
⚠️ Problème : on a donc le moyen de créer deux instances, car MyClass est toujours constructible par elle même.
Approche conceptuelle 1
On privatise le constructeur et on on compte les instance dans l'ObjectSpace de Ruby.
Il s'agit d'une méthode introspective.
ℹ️ Remarque : SingletonClass.instance est donc la factory de l'objet unique de classe SingletonClass
class SingletonClass
private_class_method :new
def SingletonClass.instance(*args, &block)
if ObjectSpace.each_object(SingletonClass).count == 0 then
return new(*args, &block)
else
return ObjectSpace.each_object(SingletonClass).first
end
end
end
p test = SingletonClass.instance
p test = SingletonClass.instance
Sortie :
#<MyClass:0x0000559b3b3780f8>
#<MyClass:0x0000559b3b3780f8>
Approche conceptuelle 2
On va utiliser un attribut de classe et privatiser le constructeur.
class SingletonClass
private_class_method :new
@@inst = nil
def SingletonClass.instance(*args, &block)
@@inst ||= new(*args, &block)
return @@inst
end
end
p test = SingletonClass.instance
p test = SingletonClass.instance
Sortie :
#<MyClass:0x0000559b3b3780f8>
#<MyClass:0x0000559b3b3780f8>
Approche Rock-solid (Thread Safe) et Ruby-way
require 'singleton'
class Config
include Singleton
def foo
puts 'foo'
end
end
p config = Config.instance
p config2 = Config.instance
Sortie :
#<MyClass:0x0000559b3b3780f8>
#<MyClass:0x0000559b3b3780f8>
On utilise le module Singletion
de la STDlib
Ruby singleton