Design pattern Singleton (Object)

22 juin 2025
🎯 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

Romain GEORGES

Open Source evangelist & Ruby enthousiast